1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【Vuejs】845- Vuejs開發(fā)移動端經(jīng)驗總結(jié)

        共 9472字,需瀏覽 19分鐘

         ·

        2021-01-21 09:21

        作者:阡ゼ陌
        https://blog.csdn.net/weixin_44705688/article/details/103961664

        移動端適配

        相對于PC端來說,移動端設(shè)備分辨率百花齊放,千奇百怪,對于每一個開發(fā)者來說,移動端適配是我們進(jìn)行移動端開發(fā)第一個需要面對的問題。

        在移動端我們經(jīng)??梢栽趆ead標(biāo)簽中看到這段代碼:

        'viewport'?content='width=device-width,initial-scale=1,user-scale=no'?/>

        通過meta標(biāo)簽對viewport的設(shè)置,定義了頁面的縮放比例;要了解這些參數(shù)的意義,我們需要先知道幾個視口寬度的意義。

        • layoutviewport布局寬度,就是網(wǎng)頁的寬度
        • visualviewport可是寬度,就是瀏覽器窗口的寬度,這個值決定了我們手機(jī)一屏能看到的內(nèi)容;visualviewport和layoutviewport的
        • 大小關(guān)系,決定了是否會出現(xiàn)滾動條,當(dāng)visualviewport更大或者剛好等于layoutviewport時是不會出現(xiàn)滾動條的。
        • idealviewport為瀏覽器定義的可完美適配移動端的viewport,固定不變,可以認(rèn)為是設(shè)備視口寬度device-width。

        meta的設(shè)置其實就是對layoutviewport和visualviewport進(jìn)行設(shè)置。

        • width=device-width表示頁面寬度layoutviewport與設(shè)備視口寬度idealviewport一致
        • initial-scale=1表示頁面寬度和網(wǎng)頁寬度與設(shè)備視口寬度的初始縮放比例,visualviewport由這個比例決定,但是對于layoutviewport來說,它同時受到兩個屬性的影響,然后取其中較大的那個值。
          user-scale=no禁止縮放

        所以現(xiàn)在我們知道,這段在移動端常見的代碼的意思,即將visualviewport和layoutviewport設(shè)置為idealviewport的值;這樣我們在移動端就不會出現(xiàn)滾動條,網(wǎng)頁內(nèi)容可以比較好的展示出來,在這個前提下我們再考慮頁面的適配問題。

        UI出圖的時候一般是有一個固定的寬度的,而我們實際的移動端設(shè)備的寬度卻都不太一樣,但是如果頁面元素的縮放比例和頁面寬度的縮放比例一致,在不同尺寸的設(shè)備下我們網(wǎng)頁的效果也將會是一致的。

        使用相對單位

        rem
        rem 是相對于根元素 html 的 font-size 來做計算。通常在頁面初始化時加載時通過對document.documentElement.style.fontSize 設(shè)置來實現(xiàn)。一般我們將根元素html的font-size設(shè)置為寬度的1/10,不同設(shè)備的寬度不同,但是同樣數(shù)值的rem比例與設(shè)備的寬度比例是一致的。

        document.documentElement.style.fontSize?=?document.documentElement.clientWidth?/?10?+?'px';

        在實際項目中我們無須在開發(fā)中自己進(jìn)行轉(zhuǎn)換,可以使用pxtorem在輸出的時候?qū)x轉(zhuǎn)換為rem。

        視口單位

        將視口寬度window.innerWidth和視口高度window.innerHeight(即layoutviewport)等分為 100 份。

        vw : 1vw 為視口寬度的 1% vh : 1vh 為視口高度的 1% vmin : vw 和 vh 中的較小值 vmax : 選取 vw 和 vh 中的較大值

        和rem相比較,視口單位不需要使用js對根元素進(jìn)行設(shè)置,兼容性稍差,但是大部分設(shè)備都已經(jīng)支持了,同樣的無須再開發(fā)時進(jìn)行單位換算,直接使用相關(guān)的插件postcss-px-to-viewport在輸出的時候進(jìn)行轉(zhuǎn)換。

        修改viewport

        之前我們提到了layoutviewport布局寬度實際上不是一個固定值,而是通過meta設(shè)置屬性,通過idealviewport計算出來的值,我們可以通過控制meta的屬性來將layoutviewport固定為某一個值。一般設(shè)計圖的寬度為750px,現(xiàn)在我們的目標(biāo)就是將layoutviewport設(shè)置為750px;layoutviewport受到兩個屬性的影響,width屬性我們之間設(shè)置為750,initial-scale縮放比例應(yīng)該為idealviewport的寬度/750;當(dāng)我們未改變meta標(biāo)簽屬性的時候,layoutviewport的值其實就是idealviewport的值,所以可以通過document.body.clientWidth或者window.innerWidth來獲取。

        ;(function?()?{
        ????const?width?=?document.body.clientWidth?||?window.innerWidth
        ????const?scale?=?width?/?750
        ????const?content?=?'width=750,?initial-scale='?+?scale?+?',?minimum-scale='?+?scale?+?',?maximum-scale='?+?scale?+?',?viewport-fit=cover'
        ????document.querySelector('meta[name="viewport"]').content?=?content
        })()

        設(shè)置完成之后,layoutviewport在不同的設(shè)備中會始終保持為750px,我們開發(fā)時可以直接使用設(shè)計稿尺寸。

        布局樣式

        布局的方式可以是各種各樣的,但是出于兼容性的考慮,有些樣式我們最好避免使用,難以解決的問題,那就不去面對。

        需要謹(jǐn)慎對待的fixed

        position:fixed在日常的頁面布局中非常常用,在許多布局中起到了關(guān)鍵的作用。它的作用是:position:fixed的元素將相對于屏幕視口(viewport)的位置來指定其位置。并且元素的位置在屏幕滾動時不會改變。但是,在許多特定的場合,position:fixed的表現(xiàn)與我們想象的大相徑庭。

        1. iOS彈出鍵盤;軟鍵盤喚起后,頁面的 fixed元素將失效(iOS認(rèn)為用戶更希望的是元素隨著滾動而移動,也就是變成了 absolute定位),既然變成了absolute,所以當(dāng)頁面超過一屏且滾動時,失效的 fixed 元素就會跟隨滾動了。
        2. 當(dāng)元素祖先的 transform 屬性非 none時,定位容器由視口改為該祖先。說的簡單點,就是position:fixed的元素會相對于最近的并且應(yīng)用了transform的祖先元素定位,而不是窗口。導(dǎo)致這個現(xiàn)象的原因是使用了transform的元素將創(chuàng)建一個新的堆疊上下文。堆疊上下文(Stacking Context):堆疊上下文是 HTML 元素的三維概念,這些 HTML 元素在一條假想的相對于面向(電腦屏幕的)視窗或者網(wǎng)頁的用戶的z 軸上延伸,HTML元素依據(jù)其自身屬性按照優(yōu)先級順序占用層疊上下文的空間。順序如下圖所示,總之堆疊上下文會對定位關(guān)系產(chǎn)生影響。想要進(jìn)一步可以查看不受控制的position:fixed。

        鍵盤彈出與使用transform屬性的情況在移動端是很常見的,所以需要謹(jǐn)慎使用position:fixed。

        推薦使用flex

        flex,即彈性布局,移動端兼容性較好,能夠滿足大部分布局需求?,F(xiàn)在我們使用flex來實現(xiàn)h5中常見的頂部標(biāo)題欄+中部滾動內(nèi)容+底部導(dǎo)航欄的布局

        頁面跳轉(zhuǎn)

        轉(zhuǎn)場動畫

        在vue中我們通過vue-router來管理路由,每個路由跳轉(zhuǎn)類似與在不同的頁面之間進(jìn)行切換,從用戶友好的角度來說,每次切換頁面的時候最好添加一個轉(zhuǎn)場效果。如果轉(zhuǎn)場動畫不區(qū)分路由是打開新頁面、還是返回之前頁面我們只需要在外使用添加一個動畫效果即可;但是一般打開和返回是應(yīng)用不同的動畫效果的,所以我們需要在切換路由的時候區(qū)分路由是前進(jìn)還是后退。為了區(qū)分路由的動作,我們在路由文件中設(shè)置meta為數(shù)字,meta表示其路由的深度,然后監(jiān)聽$route,根據(jù)to、from meta值的大小設(shè)置不同的跳轉(zhuǎn)動畫。如果應(yīng)用到多種跳轉(zhuǎn)動畫,可以根據(jù)詳情,具體情況具體應(yīng)用。




        登錄跳轉(zhuǎn)

        雖然這樣能夠?qū)崿F(xiàn)跳轉(zhuǎn)效果,但是需要在編寫router時添加設(shè)置,比較麻煩;我們可以使用開源項目vue-navigation來實現(xiàn),更加方便,無須對router進(jìn)行多余的設(shè)置。npm i -S vue-navigation安裝,在main.js中導(dǎo)入:

        import?Navigation?from?'vue-navigation'
        Vue.use(Navigation,?{router})?//?router為路由文件

        在App.vue中設(shè)置:

        this.$navigation.on('forward',?(to,?from)?=>?{
        ????this.transitionName?=?'fade-right'
        ?})
        ?this.$navigation.on('back',?(to,?from)?=>?{
        ????this.transitionName?=?'fade-left'
        ?})
        ?this.$navigation.on('replace',?(to,?from)?=>?{
        ????this.transitionName?=?'fade'
        ?})

        vue-navigation插件還有一個重要的功能就是保存頁面狀態(tài),與keep-alive相似,但是keep-alive保存狀態(tài)無法識別路由的前進(jìn)后退,而實際應(yīng)用中,我們的需求是返回頁面時,希望頁面狀態(tài)保存,當(dāng)進(jìn)入頁面時希望獲取新的數(shù)據(jù),使用vue-navigation可以很好的實現(xiàn)這個效果。具體使用可以查看vue-navigation有詳細(xì)使用說明與案例。另外也可以嘗試vue-page-stack,兩個項目都能實現(xiàn)我們需要的效果,vue-page-stack借鑒了vue-navigation,也實現(xiàn)了更多的功能,并且最近也一直在更新。

        PS: 這里的動畫效果引用自animate.scss;

        底部導(dǎo)航欄

        之前我們已經(jīng)實現(xiàn)了底部導(dǎo)航欄的基本樣式,這里我們再做一些說明。當(dāng)頁面路由路徑與router-link的路由匹配時,router-link將會被設(shè)置為激活狀態(tài),我們可以通過設(shè)置active-class來設(shè)置路徑激活時應(yīng)用的類名,默認(rèn)為router-link-active,而激活的類名還有一個router-link-exact-active,這個類名是由exact-active-class來設(shè)置的,同樣是設(shè)置路徑激活時應(yīng)用的類名;active-class與exact-active-class其實是由路由的匹配方式?jīng)Q定的。

        一般路由的匹配方式是包含匹配。舉個例子,如果當(dāng)前的路徑是 /a 開頭的,那么 也會被設(shè)置 CSS 類名。按照這個規(guī)則,每個路由都會激活 ,而使用exact屬性可以使用“精確匹配模式”。精確匹配只有當(dāng)路由完全相同的時候才會被激活。

        路由守衛(wèi)

        移動端的路由守衛(wèi)一般不會太復(fù)雜,主要是登錄權(quán)限的判斷,我們設(shè)置一個路由白名單,將所有不需要登錄權(quán)限的路由放入其中;對于需要登錄的路由做判斷,沒有登錄就跳轉(zhuǎn)登錄頁面,要求用戶進(jìn)行登錄后在訪問,如果登錄后需要返回原有路由就把目標(biāo)頁面的路由作為參數(shù)傳遞給登錄頁面,再在登錄后進(jìn)行判斷,如果存在目標(biāo)頁面參數(shù)就跳轉(zhuǎn)目標(biāo)頁面,沒有就跳轉(zhuǎn)首頁。

        如果你的應(yīng)用涉及到權(quán)限,那需要標(biāo)注每個路由需要的權(quán)限,在meta中設(shè)置roles,roles是數(shù)組來保存需要的權(quán)限;從后臺的接口中獲取用戶擁有的權(quán)限和roles進(jìn)行對比就可以判斷是否具有相關(guān)權(quán)限了。

        const?whiteList?=?['/login']
        router.beforeEach((to,?from,?next)?=>?{
        ??const?hasToken?=?store.getters.auth
        ??if?(hasToken)?{
        ????if?(to.path?===?'/login')?{
        ??????next({?path:?'/'?})
        ????}?else?{
        ??????const?needRoles?=?to.meta?&&?to.meta.roles?&&?to.meta.roles.length?>?0
        ??????if?(needRoles)?{
        ????????const?hasRoles?=?store.state.user.roles.some(role?=>?to.meta.roles.includes(role))
        ????????if?(hasRoles)?{
        ??????????next()
        ????????}?else?{
        ??????????next('/403')
        ????????}
        ??????}?else?{
        ????????next()
        ??????}
        ????}
        ??}?else?{
        ????if?(whiteList.includes(to.path))?{
        ??????next()
        ????}?else?{
        ??????next('/login')
        ????}
        ??}
        })

        組件

        自動加載

        在我們的項目中,往往會使用的許多組件,一般使用頻率比較高的組件為了避免重復(fù)導(dǎo)入的繁瑣一般是作為全局組件在項目中使用的。而注冊全局組件我們首先需要引入組件,然后使用Vue.component進(jìn)行注冊;這是一個重復(fù)的工作,我們每次創(chuàng)建組件都會進(jìn)行,如果我們的項目是使用webpack構(gòu)建(vue-cli也是使用webpack),我們就可以通過require.context自動將組件注冊到全局。創(chuàng)建components/index.js文件:

        export?default?function?registerComponent?(Vue)?{
        ??/**
        ???*?參數(shù)說明:
        ???*?1.?其組件目錄的相對路徑
        ???*?2.?是否查詢其子目錄
        ???*?3.?匹配基礎(chǔ)組件文件名的正則表達(dá)式
        ???**/
        ??const?modules?=?require.context('./',?false,?/\w+.vue$/)
        ??modules.keys().forEach(fileName?=>?{
        ????//?獲取組件配置
        ????const?component?=?modules(fileName)
        ????//?獲取組件名稱,去除文件名開頭的?`./`?和結(jié)尾的擴(kuò)展名
        ????const?name?=?fileName.replace(/^\.\/(.*)\.\w+$/,?'$1')
        ????//?注冊全局組件
        ????//?如果這個組件選項是通過?`export?default`?導(dǎo)出的,
        ????//?那么就會優(yōu)先使用?`.default`,
        ????//?否則回退到使用模塊的根。
        ????Vue.component(name,?component.default?||?component)
        ??})
        }

        之后在main.js中導(dǎo)入注冊模塊進(jìn)行注冊,使用require.context我們也可以實現(xiàn)vue插件和全局filter的導(dǎo)入。

        import?registerComponent?from?'./components'
        registerComponent(Vue)

        通過v-model綁定數(shù)據(jù)

        v-model是語法糖,它的本質(zhì)是對組件事件進(jìn)行監(jiān)聽和數(shù)據(jù)進(jìn)行更新,是props和 o n 監(jiān) 聽 事 件 的 縮 寫 , v ? m o d e l 默 認(rèn) 傳 遞 v a l u e , 監(jiān) 聽 i n p u t 事 件 。現(xiàn) 在 我 們 使 用 v ? m o d e l 來 實 現(xiàn) 下 數(shù) 字 輸 入 框 , 這 個 輸 入 框 只 能 輸 入 數(shù) 字 , 在 組 件 中 我 們 只 需 要 定 義 v a l u e 來 接 受 傳 值 , 然 后 在 輸 入 值 滿 足 我 們 輸 入 條 件 ( 輸 入 為 數(shù) 字 ) 的 時 候 使 用 on監(jiān)聽事件的縮寫,v-model默認(rèn)傳遞value,監(jiān)聽input事件?,F(xiàn)在我們使用v-model來實現(xiàn)下數(shù)字輸入框,這個輸入框只能輸入數(shù)字,在組件中我們只需要定義value來接受傳值,然后在輸入值滿足我們輸入條件(輸入為數(shù)字)的時候使用 on監(jiān)聽事件的縮寫,v?model默認(rèn)傳遞value,監(jiān)聽input事件?,F(xiàn)在我們使用v?model來實現(xiàn)下數(shù)字輸入框,這個輸入框只能輸入數(shù)字,在組件中我們只需要定義value來接受傳值,然后在輸入值滿足我們輸入條件(輸入為數(shù)字)的時候使用emit觸發(fā)input事件。



        使用的時候,我們只需要使用v-model綁定值就可以了。v-model默認(rèn)會利用名為value的prop和名為input的事件,但是很多時候我們想使用不同的prop和監(jiān)聽不同的事件,我們可以使用model選項進(jìn)行修改。

        Vue.component('my-checkbox',?{
        ??model:?{
        ????prop:?'checked',
        ????event:?'change'
        ??},
        ??props:?{
        ????//?this?allows?using?the?`value`?prop?for?a?different?purpose
        ????value:?String,
        ????//?use?`checked`?as?the?prop?which?take?the?place?of?`value`
        ????checked:?{
        ??????type:?Number,
        ??????default:?0
        ????}
        ??},
        ??//?...
        })
        "foo"?value="some?value">

        上述代碼相當(dāng)于:

        ??:checked="foo"
        ??@change="val?=>?{?foo?=?val?}"
        ??value="some?value">

        通過插件的方式來使用組件

        在很多第三方組件庫中,我們經(jīng)??吹街苯邮褂貌寮姆绞秸{(diào)用組件的方式,比如VantUI的Dialog彈出框組件,我們不但可以使用組件的方式進(jìn)行使用,也可以通過插件的形式進(jìn)行調(diào)用。

        this.$dialog.alert({
        ??message:?'彈窗內(nèi)容'
        });

        將組件作為插件使用的原理其實并不復(fù)雜,就是使用手動掛載Vue組件實例。

        import?Vue?from?'vue';
        export?default?function?create(Component,?props)?{
        ????//?先創(chuàng)建實例
        ????const?vm?=?new?Vue({
        ????????render(h)?{
        ????????????//?h就是createElement,它返回VNode
        ????????????return?h(Component,?{props})
        ????????}
        ????}).$mount();
        ????//?手動掛載
        ????document.body.appendChild(vm.$el);
        ????//?銷毀方法
        ????const?comp?=?vm.$children[0];
        ????comp.remove?=?function()?{
        ????????document.body.removeChild(vm.$el);
        ????????vm.$destroy();
        ????}
        ????return?comp;
        }

        調(diào)用create傳入組件和props參數(shù)就可以獲取組件的實例,通過組件實例我們就可以調(diào)用組件的各種功能了。



        "css"?scoped>
        .loading-wrapper?{
        ??position:?absolute;
        ??top:?0;
        ??bottom:?0;
        ??width:?100%;
        ??background-color:?rgba(0,?0,?0,?.4);
        ??z-index:?999;
        }


        const?loading?=?create(Loading,?{})
        loading.show()?//?顯示
        loading.hide()?//?關(guān)閉

        第三方組件

        移動端各種組件、插件已經(jīng)相對完善,在項目開發(fā)中重復(fù)造輪子是一件很不明智的事情;開發(fā)項目時我們可以借助第三方組件、插件提高我們的開發(fā)效率。

        常用組件庫
        VantUI是有贊開源的一套輕量、可靠的移動端Vue組件庫;支持按需引入、主題定制、SSR,除了常用組件外,針對電商場景還有專門的業(yè)務(wù)組件,如果是開發(fā)電商項目的話,推薦使用。官方文檔關(guān)于主題定制是在webpack.config.js中進(jìn)行設(shè)置的:

        //?webpack.config.js
        module.exports?=?{
        ??rules:?[
        ????{
        ??????test:?/\.less$/,
        ??????use:?[
        ????????//?...其他?loader?配置
        ????????{
        ??????????loader:?'less-loader',
        ??????????options:?{
        ????????????modifyVars:?{
        ??????????????//?直接覆蓋變量
        ??????????????'text-color':?'#111',
        ??????????????'border-color':?'#eee'
        ??????????????//?或者可以通過?less?文件覆蓋(文件路徑為絕對路徑)
        ??????????????'hack':?`true;?@import?"your-less-file-path.less";`
        ????????????}
        ??????????}
        ????????}
        ??????]
        ????}
        ??]
        };

        但我們的項目可能是使用vue-cli構(gòu)建,這時我們需要在vue.config.js中進(jìn)行設(shè)置:

        module.exports?=?{
        ??css:?{
        ????loaderOptions:?{
        ??????less:?{
        ????????modifyVars:?{
        ??????????'hack':?`true;?@import?"~@/assets/less/vars.less";`
        ????????}
        ??????}
        ????}
        ??}
        }

        另外vux、mint-ui也是很好的選擇。

        常用插件

        better-scroll是一個為移動端各種滾動場景提供絲滑的滾動效果的插件,如果在vue中使用可以參考作者的文章當(dāng) better-scroll 遇見 Vue。

        swiper是一個輪播圖插件,如果是在vue中使用可以直接使用vue-awesome-swiper,vue-awesome-swiper基于Swiper4,并且支持SSR。

        1. JavaScript 重溫系列(22篇全)
        2. ECMAScript 重溫系列(10篇全)
        3. JavaScript設(shè)計模式 重溫系列(9篇全)
        4.?正則 / 框架 / 算法等 重溫系列(16篇全)
        5.?Webpack4 入門(上)||?Webpack4 入門(下)
        6.?MobX 入門(上)?||??MobX 入門(下)
        7. 100+篇原創(chuàng)系列匯總

        回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~

        點擊“閱讀原文”查看 100+ 篇原創(chuàng)文章

        瀏覽 95
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美日本在线视频 | 搡老女人免费国产一级 | 内射北条麻妃 | 久久久久成人精品 | 综合五月网 | aaaaaa做受片在线观看 | 在线色网 | 国产 在线观看免费视频 | 日韩三级电影在线看 | av777777 |