<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于Vue的前端架構(gòu),我總結(jié)15點(diǎn)經(jīng)驗(yàn)

          共 15400字,需瀏覽 31分鐘

           ·

          2020-12-11 00:53

          (給前端大學(xué)加星標(biāo),提升前端技能.

          作者:codexu

          https://juejin.cn/post/6901466994478940168

          1.分解需求


          技術(shù)棧

          • 考慮到后續(xù)招人和現(xiàn)有人員的技術(shù)棧,選擇 Vue 作為框架。

          • 公司主要業(yè)務(wù)是 GIS 和 BIM,通常開發(fā)一些中大型的系統(tǒng),所以 vue-router 和 vuex 都是必不可少的。

          • 放棄了 Element UI 選擇了 Ant Design Vue(最近 Element 好像復(fù)活了,麻蛋)。

          • 工具庫選擇 lodash。


          建立腳手架

          • 搭建 NPM 私服。

          • 使用 Node 環(huán)境開發(fā) CLI 工具,參考我自己寫過的一篇 -【 搭建自己的腳手架—“優(yōu)雅”生成前端工程】。

          • 基于 @vue/cli 搭建基礎(chǔ)的模板(大家都比較了解,節(jié)省開發(fā)時(shí)間,遠(yuǎn)勝于從零開始搭建)。

          • 根據(jù)業(yè)務(wù)需求定義各種開發(fā)中可能用到的功能(組件庫、狀態(tài)管理、過濾器、指令、CSS內(nèi)置變量、CSS Mixins、表單驗(yàn)證、工具函數(shù)等)。

          • 性能優(yōu)化,例如對(duì) Ant Design Vue 組件庫的優(yōu)化。


          開發(fā)規(guī)范

          • 對(duì)代碼風(fēng)格、命名規(guī)則、目錄結(jié)構(gòu)進(jìn)行統(tǒng)一規(guī)范。

          • 靜態(tài)資源的使用規(guī)范。

          • 單元測(cè)試、提交線上測(cè)試規(guī)范。

          • Git 提交記錄和多人協(xié)作規(guī)范。


          2.樣式


          CSS 預(yù)處理器的選擇

          • Sass/Scss ?

          • Less ?

          • Stylus ?

          為什么選擇了兩個(gè)?因?yàn)楣緢F(tuán)隊(duì)跟傾向于使用 scss 開發(fā),less 是為了覆蓋 ant design vue 的樣式,stylus 只有我自己喜歡這種風(fēng)格。


          局部樣式與全局樣式

          局部樣式

          一般都是使用 scoped 方案:


          全局樣式

          全局樣式 目錄:@/styles

          variable.scss: 全局變量管理 mixins.scss: 全局 Mixins 管理 global.scss: 全局樣式

          其中 variable.scss 和 mixins.scss 會(huì)優(yōu)先于 global.css 加載,并且可以不通過 import 的方式在項(xiàng)目中任何位置使用這些變量和 mixins。

          // vue.config.jsmodule.exports = {  css: {    loaderOptions: {      sass: {        prependData: `        @import '@/styles/variable.scss';        @import '@/styles/mixins.scss';        `,      },    },  },}

          體驗(yàn)優(yōu)化

          頁面載入進(jìn)度條

          使用 nprogress 對(duì)路由跳轉(zhuǎn)時(shí)做一個(gè)偽進(jìn)度條,這樣做在網(wǎng)絡(luò)不好的情況下可以讓用戶知道頁面已經(jīng)在加載了:

          import NProgress from 'nprogress';
          router.beforeEach(() => { NProgress.start();});
          router.afterEach(() => { NProgress.done();});

          美化滾動(dòng)條

          一直用 Mac 做前端,突然發(fā)現(xiàn)同事的 Windows 上出現(xiàn)了十分丑陋的滾動(dòng)條,為了保持一致:

          ::-webkit-scrollbar {  width: 6px;  height: 6px;}
          ::-webkit-scrollbar-track { width: 6px; background: rgba(#101F1C, 0.1); -webkit-border-radius: 2em; -moz-border-radius: 2em; border-radius: 2em;}
          ::-webkit-scrollbar-thumb { background-color: rgba(#101F1C, 0.5); background-clip: padding-box; min-height: 28px; -webkit-border-radius: 2em; -moz-border-radius: 2em; border-radius: 2em;}
          ::-webkit-scrollbar-thumb:hover { background-color: rgba(#101F1C, 1);}

          靜態(tài)資源加載頁面

          首次加載頁面時(shí),會(huì)產(chǎn)生大量的白屏?xí)r間,這時(shí)做一個(gè) loading 效果看起來會(huì)很友好,其實(shí)很簡單,直接在 public/index.html 里寫一些靜態(tài)的樣式即可。


          移動(dòng)端 100vh 問題

          在移動(dòng)端使用 100vh 時(shí),發(fā)現(xiàn)在 Chrome、Safari 瀏覽器中,因?yàn)闉g覽器欄和一些導(dǎo)航欄、鏈接欄導(dǎo)致不一樣的呈現(xiàn):

          你以為的 100vh === 視口高度

          實(shí)際上 100vh === 視口高度 + 瀏覽器工具欄(地址欄等等)的高度


          解決方案

          安裝 vh-check npm install vh-check --save

          import vhCheck from 'vh-check';vhCheck('browser-address-bar');

          定義一個(gè) CSS Mixin

          @mixin vh($height: 100vh) {  height: $height;  height: calc(#{$height} - var(--browser-address-bar, 0px));}

          之后就是哪里不會(huì)點(diǎn)哪里。


          3.組件庫

          因?yàn)?Element UI 長期沒更新,并且之前使用過 React 的 Ant Design(重點(diǎn)),所以組件庫選擇了Ant Design Vue。


          覆蓋 Ant ?Design Vue 樣式

          設(shè)計(jì)師眼中的 Ant Design === '丑'(心酸)。


          1.使用 .less 文件

          Ant ?Design Vue 的樣式使用了 Less 作為開發(fā)語言,并定義了一系列全局/組件的樣式變量,所以需要安裝了 less、less-loader,在 @/styles/antd-theme.less 可以覆蓋默認(rèn)樣式。

          優(yōu)點(diǎn)是:

          方便快捷,可以修改 class,覆蓋默認(rèn)變量。

          缺點(diǎn)是:

          必須引入 @import '~ant-design-vue/dist/antd.less'; ,引入后會(huì)將所有的組件樣式全部引入,導(dǎo)致打包后的 css 體積達(dá)到 500kb 左右。


          2.使用 JavaScript 對(duì)象

          通過 JavaScript 對(duì)象的方式可以修改內(nèi)置變量,需要對(duì) Less 進(jìn)行配置:

          // vue.config.jsconst modifyVars = require('./src/styles/antdTheme.js');
          module.exports = { css: { loaderOptions: { less: { lessOptions: { javascriptEnabled: true, modifyVars, }, }, }, },}

          這一步還可以繼續(xù)優(yōu)化,通過 babel-plugin-import 使 Ant Design Vue 的組件樣式可以按需加載:

          // babel.config.jsmodule.exports = {  presets: [    '@vue/cli-plugin-babel/preset',  ],  plugins: [    [      'import',      { libraryName: 'ant-design-vue', libraryDirectory: 'es', style: true },    ],  ],};

          優(yōu)點(diǎn)是:

          可以按需引入,打包后的 CSS 體積取決于你引用了多少個(gè)組件。

          缺點(diǎn)是:

          不能使用 class 進(jìn)行樣式覆蓋。


          干掉無用的圖標(biāo)

          Ant Design Vue 把所有的 Icon 一次性引入(不管你因用了多少個(gè)組件),這使得體積打包后圖標(biāo)所占的體積竟然有幾百 kb 之多。這些圖標(biāo)大多數(shù)不會(huì)被設(shè)計(jì)師所采納,所以部分圖標(biāo)都應(yīng)該被干掉:

          創(chuàng)建一個(gè) icons.js 來管理 Ant ?Design Vue 圖標(biāo),這里以一個(gè) Loading 圖標(biāo)為例:

          // @/src/assets/icons.jsexport?{?default?as?LoadingOutline?}?from?'@ant-design/icons/lib/outline/LoadingOutline';

          如何知道你要加載的圖標(biāo)在什么路徑下?

          在 @ant-design/icons/lib 目錄下有三種風(fēng)格的圖標(biāo),分別是 fill、outline、twotone,這里面內(nèi)部的文件并不是 svg 格式,而是 js 和 ts 格式,這就是為什么我們可以這么引入圖標(biāo)的關(guān)鍵所在了。

          下一步是通過配置 vue.config.js 將這個(gè)文件引入進(jìn)來:

          // vue.config.jsmodule.exports = {  configureWebpack: {    resolve: {      alias: {        '@ant-design/icons/lib/dist$': path.resolve(__dirname, './src/assets/icons.js'),      },    },  },}

          解決 Moment 多國語

          解決到這之后,Ant Design Vue 居然還很大,這是因?yàn)?moment 是 Ant Design Vue 中有強(qiáng)依賴該插件,所以使用 webpack 插件減小打包體積,這里我們只保留 zh-cn 語言包:

          // vue.config.jsmodule.exports = {  chainWebpack: (config) => {    config      .plugin('ContextReplacementPlugin')      .use(webpack.ContextReplacementPlugin, [/moment[/\\]locale$/, /zh-cn/]);  },}

          部分組件需要在頁面內(nèi)引用

          Ant Design Vue 中部分體積較大的組件,例如 DatePicker,根據(jù)業(yè)務(wù)需求,應(yīng)考慮在頁面中進(jìn)行加載,盡量保證首屏加載的速度:


          4.靜態(tài)資源與圖標(biāo)


          靜態(tài)資源

          所有的靜態(tài)資源文件都會(huì)上傳到 阿里云 OSS 上,所以在環(huán)境變量上加以區(qū)分。

          .env.development .env.productionVUE_APP_STATIC_URL 屬性分別配置了本地的靜態(tài)資源服務(wù)器地址和線上 OSS 的地址。

          本地的靜態(tài)資源服務(wù)器是通過 pm2 + http-server 創(chuàng)建的,設(shè)計(jì)師切完直接扔進(jìn)來就好了。


          自動(dòng)注冊(cè) Svg 圖標(biāo)

          在日常的開發(fā)中,總是會(huì)有著大量的圖標(biāo)需要使用,這里我們直接選擇使用 SVG 圖標(biāo)。但是如果每次使用圖標(biāo)還需要通過路徑找到這張圖標(biāo)豈不是很麻煩?

          下面這種才是我想要的方案(直接 name 等于 文件名即可):

          而且最后打包后需要合并成一張雪碧圖。

          首先需要對(duì) ?@/assets/icons 文件夾下的 svg 圖標(biāo)進(jìn)行自動(dòng)注冊(cè),需要對(duì) webpack 和 svg-sprite-loader 進(jìn)行了相關(guān)設(shè)置,文件全部打包成 svg-sprite。

          module.exports = {  chainWebpack: (config) => {    config.module      .rule('svg')      .exclude.add(resolve('src/assets/icons'))      .end();
          config.module .rule('icons') .test(/\.svg$/) .include.add(resolve('src/assets/icons')) .end() .use('svg-sprite-loader') .loader('svg-sprite-loader'); },}

          寫一個(gè)全局用的 Vue 組件 :

          // @/components/m-svg/index.jsconst requireAll = (requireContext) => requireContext.keys().map(requireContext);const req = require.context('@/assets/icons', false, /\.svg$/);requireAll(req);

          @/components/m-svg/index.vue

          參數(shù) name

          • 類型:String

          • 默認(rèn)值:null

          • 說明:放置在 @/assets/icons 文件夾下的文件名

          樣式

          • 圖標(biāo)的大小可以通過 width + height 屬性改變。

          • 通過改變 font-size 屬性改變,寬高 = font-zise * 1.4


          5.異步請(qǐng)求


          封裝 Axios

          @/libs/request.js 路徑下對(duì) Axios 進(jìn)行封裝,封裝了請(qǐng)求參數(shù),請(qǐng)求頭,以及錯(cuò)誤提示信息、 request 攔截器、response 攔截器、統(tǒng)一的錯(cuò)誤處理、baseURL 設(shè)置等。

          廢話不說直接貼代碼:

          import axios from 'axios';import get from 'lodash/get';import storage from 'store';// 創(chuàng)建 axios 實(shí)例const request = axios.create({ // API 請(qǐng)求的默認(rèn)前綴 baseURL: process.env.VUE_APP_BASE_URL, timeout: 10000, // 請(qǐng)求超時(shí)時(shí)間});
          // 異常攔截處理器const errorHandler = (error) => { const status = get(error, 'response.status'); switch (status) { /* eslint-disable no-param-reassign */ case 400: error.message = '請(qǐng)求錯(cuò)誤'; break; case 401: error.message = '未授權(quán),請(qǐng)登錄'; break; case 403: error.message = '拒絕訪問'; break; case 404: error.message = `請(qǐng)求地址出錯(cuò): ${error.response.config.url}`; break; case 408: error.message = '請(qǐng)求超時(shí)'; break; case 500: error.message = '服務(wù)器內(nèi)部錯(cuò)誤'; break; case 501: error.message = '服務(wù)未實(shí)現(xiàn)'; break; case 502: error.message = '網(wǎng)關(guān)錯(cuò)誤'; break; case 503: error.message = '服務(wù)不可用'; break; case 504: error.message = '網(wǎng)關(guān)超時(shí)'; break; case 505: error.message = 'HTTP版本不受支持'; break; default: break; /* eslint-disabled */ } return Promise.reject(error);};
          // request interceptorrequest.interceptors.request.use((config) => { // 如果 token 存在 // 讓每個(gè)請(qǐng)求攜帶自定義 token 請(qǐng)根據(jù)實(shí)際情況自行修改 // eslint-disable-next-line no-param-reassign config.headers.Authorization = `bearer ${storage.get('ACCESS_TOKEN')}`; return config;}, errorHandler);
          // response interceptorrequest.interceptors.response.use((response) => { const dataAxios = response.data; // 這個(gè)狀態(tài)碼是和后端約定的 const { code } = dataAxios; // 根據(jù) code 進(jìn)行判斷 if (code === undefined) { // 如果沒有 code 代表這不是項(xiàng)目后端開發(fā)的接口 return dataAxios; // eslint-disable-next-line no-else-return } else { // 有 code 代表這是一個(gè)后端接口 可以進(jìn)行進(jìn)一步的判斷 switch (code) { case 200: // [ 示例 ] code === 200 代表沒有錯(cuò)誤 return dataAxios.data; case 'xxx': // [ 示例 ] 其它和后臺(tái)約定的 code return 'xxx'; default: // 不是正確的 code return '不是正確的code'; } }}, errorHandler);
          export default request;
          • 通過 VUE_APP_BASE_URL 區(qū)分線上與開發(fā)環(huán)境的 API 地址。

          • code 起到一個(gè)比較關(guān)鍵的作用,例如 token 過期時(shí)的驗(yàn)證。

          • 使用了一個(gè)叫 sotre 的包作為本地儲(chǔ)存的工具用來存儲(chǔ) token。


          跨域問題

          跨域問題一般情況直接找后端解決了,你要是不好意思打擾他們的話,可以用 devServer 提供的 proxy 代理:

          // vue.config.jsdevServer: {  proxy: {    '/api': {      target: 'http://47.100.186.132/your-path/api',      ws: true,      changeOrigin: true,      pathRewrite: {        '^/api': ''      }    }  }}

          Mock 數(shù)據(jù)

          一個(gè)很常見的情況,后端接口沒出來,前端在這干瞪眼。

          Mock 數(shù)據(jù)功能是基于 mock.js (opens new window)開發(fā),通過 webpack 進(jìn)行自動(dòng)加載 mock 配置文件。


          規(guī)則

          • 所有的 mock 配置文件均應(yīng)放置在 @/mock/services 路徑內(nèi)。

          • @/mock/services 內(nèi)部可以建立業(yè)務(wù)相關(guān)的文件夾分類存放配置文件。

          • 所有的配置文件應(yīng)按照 ***.mock.js 的命名規(guī)范創(chuàng)建。

          • 配置文件使用 ES6 Module 導(dǎo)出 export defaultexport 一個(gè)數(shù)組。


          入口文件

          import Mock from 'mockjs';
          Mock.setup({ timeout: '500-800',});
          const context = require.context('./services', true, /\.mock.js$/);
          context.keys().forEach((key) => { Object.keys(context(key)).forEach((paramKey) => { Mock.mock(...context(key)[paramKey]); });});

          示例模板

          import Mock from 'mockjs';
          const { Random } = Mock;
          export default [ RegExp('/example.*'), 'get', { 'range|50-100': 50, 'data|10': [ { // 唯一 ID id: '@guid()', // 生成一個(gè)中文名字 cname: '@cname()', // 生成一個(gè) url url: '@url()', // 生成一個(gè)地址 county: Mock.mock('@county(true)'), // 從數(shù)組中隨機(jī)選擇一個(gè)值 'array|1': ['A', 'B', 'C', 'D', 'E'], // 隨機(jī)生成一個(gè)時(shí)間 time: '@datetime()', // 生成一張圖片 image: Random.dataImage('200x100', 'Mock Image'), }, ], },];

          6.路由


          Layout

          布局暫時(shí)分為三大類:

          • frameIn:基于 BasicLayout,通常需要登錄或權(quán)限認(rèn)證的路由。

          • frameOut:不需要?jiǎng)討B(tài)判斷權(quán)限的路由,如登錄頁或通用頁面。

          • errorPage:例如404。


          權(quán)限驗(yàn)證

          通過獲取當(dāng)前用戶的權(quán)限去比對(duì)路由表,生成當(dāng)前用戶具的權(quán)限可訪問的路由表,通過 router.addRoutes 動(dòng)態(tài)掛載到 router 上。

          • 判斷頁面是否需要登陸狀態(tài),需要?jiǎng)t跳轉(zhuǎn)到 /user/login

          • 本地存儲(chǔ)中不存在 token 則跳轉(zhuǎn)到 /user/login

          • 如果存在 token,用戶信息不存在,自動(dòng)調(diào)用 vuex '/system/user/getInfo'

          在路由中,集成了權(quán)限驗(yàn)證的功能,需要為頁面增加權(quán)限時(shí),在 meta 下添加相應(yīng)的 key:


          auth

          • 類型:Boolean

          • 說明:當(dāng) auth 為 true 時(shí),此頁面需要進(jìn)行登陸權(quán)限驗(yàn)證,只針對(duì) frameIn 路由有效。


          permissions

          • 類型:Object

          • 說明:permissions 每一個(gè) key 對(duì)應(yīng)權(quán)限功能的驗(yàn)證,當(dāng) key 的值為 true 時(shí),代表具有權(quán)限,若 key 為 false,配合 v-permission 指令,可以隱藏相應(yīng)的 DOM。

          在這里貼一下路由跳轉(zhuǎn)時(shí)權(quán)限驗(yàn)證的代碼:

          import router from '@/router';import store from '@/store';import storage from 'store';import util from '@/libs/utils';
          // 進(jìn)度條import NProgress from 'nprogress';import 'nprogress/nprogress.css';
          const loginRoutePath = '/user/login';const defaultRoutePath = '/home';
          /** * 路由攔截 * 權(quán)限驗(yàn)證 */router.beforeEach(async (to, from, next) => { // 進(jìn)度條 NProgress.start(); // 驗(yàn)證當(dāng)前路由所有的匹配中是否需要有登錄驗(yàn)證的 if (to.matched.some((r) => r.meta.auth)) { // 是否存有token作為驗(yàn)證是否登錄的條件 const token = storage.get('ACCESS_TOKEN'); if (token && token !== 'undefined') { // 是否處于登錄頁面 if (to.path === loginRoutePath) { next({ path: defaultRoutePath }); // 查詢是否儲(chǔ)存用戶信息 } else if (Object.keys(store.state.system.user.info).length === 0) { store.dispatch('system/user/getInfo').then(() => { next(); }); } else { next(); } } else { // 沒有登錄的時(shí)候跳轉(zhuǎn)到登錄界面 // 攜帶上登陸成功之后需要跳轉(zhuǎn)的頁面完整路徑 next({ name: 'Login', query: { redirect: to.fullPath, }, }); NProgress.done(); } } else { // 不需要身份校驗(yàn) 直接通過 next(); }});
          router.afterEach((to) => { // 進(jìn)度條 NProgress.done(); util.title(to.meta.title);});


          頁面開發(fā)

          • 根據(jù)業(yè)務(wù)需要?jiǎng)澐郑凑章酚蓪蛹?jí)在 views 中創(chuàng)建相對(duì)應(yīng)的頁面組件,以文件夾的形式創(chuàng)建,并在文件夾內(nèi)創(chuàng)建 index.vue 文件作為頁面的入口文件。

          • 頁面內(nèi)的組件:在頁面文件夾下創(chuàng)建 components 文件夾,在其內(nèi)部對(duì)應(yīng)創(chuàng)建相應(yīng)的組件文件,如果是復(fù)雜組件,應(yīng)以文件夾的形式創(chuàng)建組件。

          • 工具模塊:能夠高度抽象的工具模塊,應(yīng)創(chuàng)建在 @/src/libs 內(nèi)創(chuàng)建 js 文件。


          7、構(gòu)建優(yōu)化


          包分析工具

          構(gòu)建代碼之后,到底是什么占用了這么多空間?靠直覺猜測(cè)或者使用 webpack-bundle-analyzer。

          const WebpackBundleAnalyzer = require('webpack-bundle-analyzer');
          module.exports = { chainWebpack: (config) => { if (process.env.use_analyzer) { config .plugin('webpack-bundle-analyzer') .use(WebpackBundleAnalyzer.BundleAnalyzerPlugin); } },};


          開啟 Gzip

          對(duì),這這么一句話,后端就得支持你的 .gz 文件了,而你只需要坐著等老板夸:

          chainWebpack: (config) => {  config    .plugin('CompressionPlugin')    .use(CompressionPlugin, []);},

          路由懶加載

          這塊 @vue/cli 已經(jīng)幫忙處理好了,但也需要了解一下他的原理和如何配置。

          {  path: 'home',  name: 'Home',  component: () => import(    /* webpackChunkName: "home" */ '@/views/home/index.vue'  ),},

          webpackChunkName 這條注釋還是很有必要加的,至少你打包后知道又是哪個(gè)頁面變得又臭又大。


          Preload & Prefetch

          不清楚這兩個(gè)功能的去 @vue/cli 補(bǔ)課,這兩個(gè)功能非常有助于你處理加載的性能。


          8.測(cè)試框架

          直接使用了官方提供的 Vue Test Utils,這東西可以對(duì)組件進(jìn)行測(cè)試,很不錯(cuò)。

          寫單元測(cè)試在團(tuán)隊(duì)里其實(shí)很難推進(jìn),不知道大家怎么看。


          9.組件庫

          對(duì)于很多第三方的工具,我堅(jiān)持認(rèn)為二次封裝成 vue 插件并沒有多少開發(fā)成本,反而讓你在后續(xù)的開發(fā)中變得很靈活。

          我對(duì)以下庫進(jìn)行了 vue 插件的封裝,并提交到 npm 私服:

          • 數(shù)字動(dòng)畫

          • 代碼高亮

          • 大文件上傳(切片、斷點(diǎn)續(xù)傳、秒傳)需要與后端配合

          • 圖片預(yù)覽

          • Excel 導(dǎo)入導(dǎo)出

          • 富文本編輯器

          • Markdown 編輯器

          • 代碼編輯器

          大文件上傳有興趣的可以留言,我后續(xù)單獨(dú)拎出來詳細(xì)的寫一下這塊。


          10.Vuex

          內(nèi)置一些功能,主要是對(duì)以下這些功能做了一些封裝:

          • 用戶信息管理(儲(chǔ)存信息、對(duì) token 進(jìn)行操作等)

          • 登陸(調(diào)接口)

          • 菜單管理(儲(chǔ)存路由信息,生成菜單,模糊查詢等功能)

          • UA信息

          • 全屏操作

          • Loading

          • 日志管理(消息提醒、日志留存、日志上報(bào))


          11.過濾器

          過濾器是 Vue 提供的一個(gè)很好用的功能,聽說 vue3 沒了?

          {{ message | capitalize }}

          我寫了幾個(gè)常用的過濾器:

          • 日期時(shí)間

          • 剩余時(shí)間

          • 區(qū)分環(huán)境的鏈接(主要針對(duì)本地靜態(tài)資源服務(wù)器和 OSS )

          • 文件大小

          • 數(shù)字金額

          • 浮點(diǎn)型精度


          12.指令

          自定義指令可以提供很好的幫助:

          • 組件權(quán)限驗(yàn)證

          • 文本復(fù)制

          • 快捷鍵綁定

          • 滾動(dòng)至指定位置

          • 圖片懶加載

          • 焦點(diǎn)


          13.開發(fā)規(guī)范


          ESLint

          不管是多人合作還是個(gè)人項(xiàng)目,代碼規(guī)范都是很重要的。這樣做不僅可以很大程度地避免基本語法錯(cuò)誤,也保證了代碼的可讀性。

          這里我們采用了 Airbnb JavaScript Style Guide。

          這套規(guī)范給我的感覺就是 很嚴(yán)謹(jǐn)


          CSS 規(guī)范

          降低選擇器復(fù)雜性

          瀏覽器讀取選擇器,遵循的原則是從選擇器的右邊到左邊讀取。

          #block .text p {  color: red;}
          • 查找所有 P 元素。

          • 查找結(jié)果 1 中的元素是否有類名為 text 的父元素

          • 查找結(jié)果 2 中的元素是否有 id 為 block 的父元素


          選擇器優(yōu)先級(jí)

          內(nèi)聯(lián) > ID選擇器 > 類選擇器 > 標(biāo)簽選擇器

          • 選擇器越短越好。

          • 盡量使用高優(yōu)先級(jí)的選擇器,例如 ID 和類選擇器。

          • 避免使用通配符 *。


          使用 flexbox

          在早期的 CSS 布局方式中我們能對(duì)元素實(shí)行絕對(duì)定位、相對(duì)定位或浮動(dòng)定位。而現(xiàn)在,我們有了新的布局方式 flexbox,它比起早期的布局方式來說有個(gè)優(yōu)勢(shì),那就是性能比較好。不過 flexbox 兼容性還是有點(diǎn)問題,不是所有瀏覽器都支持它,所以要謹(jǐn)慎使用。各瀏覽器兼容性:

          • Chrome 29+

          • Firefox 28+

          • Internet Explorer 11

          • Opera 17+

          • Safari 6.1+ (prefixed with -webkit-)

          • Android 4.4+

          • iOS 7.1+ (prefixed with -webkit-)


          動(dòng)畫性能優(yōu)化

          在 CSS 中,transforms 和 opacity 這兩個(gè)屬性更改不會(huì)觸發(fā)重排與重繪,它們是可以由合成器(composite)單獨(dú)處理的屬性。


          屬性值

          • 當(dāng)數(shù)值為 0 - 1 之間的小數(shù)時(shí),建議省略整數(shù)部分的 0。

          • 當(dāng)長度為 0 時(shí)建議省略單位。

          • 建議不使用命名色值。

          • 建議當(dāng)元素需要撐起高度以包含內(nèi)部的浮動(dòng)元素時(shí),通過對(duì)偽類設(shè)置 clear 或觸發(fā) BFC 的方式進(jìn)行 clearfix。盡量不使用增加空標(biāo)簽的方式。

          • 除公共樣式之外,在業(yè)務(wù)代碼中盡量不能使用 !important。

          • 建議將 z-index 進(jìn)行分層,對(duì)文檔流外絕對(duì)定位元素的視覺層級(jí)關(guān)系進(jìn)行管理。


          字體排版

          • 字號(hào)應(yīng)不小于 12px(PC端)。

          • font-weight 屬性建議使用數(shù)值方式描述。

          • line-height 在定義文本段落時(shí),應(yīng)使用數(shù)值。


          Vue 代碼規(guī)范

          常規(guī)

          • 當(dāng)在組件中使用 data 屬性的時(shí)候 (除了 new Vue 外的任何地方),它的值必須是返回一個(gè)對(duì)象的函數(shù) data() { return {...} }

          • prop 的定義應(yīng)該盡量詳細(xì),至少需要指定其類型。

          • 布爾類型的 attribute, 為 true 時(shí)直接寫屬性值。

          • 不要在computed中對(duì)vue變量進(jìn)行操作。

          • 應(yīng)該優(yōu)先通過 prop 和事件進(jìn)行父子組件之間的通信,而不是 this.$parent 或改變 prop。

          • 在組件上總是必須用 key 配合 v-for,以便維護(hù)內(nèi)部組件及其子樹的狀態(tài)。

          • v-if 和 v-for 不能同時(shí)使用

          • 公共方法盡量不要掛到原型上, 可以寫在 utils 文件,也可以使用 mixin 文件。不要將業(yè)務(wù)公共組件注冊(cè)到全局。

          • 不要將任何第三方插件掛載到 vue 原型上。

          • 具有高度通用性的方法,要封裝到 libs、全局組件或指令集里。

          • 為組件樣式設(shè)置作用域。

          • 盡量使用指令縮寫。


          vuex

          State (opens new window)為單一狀態(tài)樹,在 state 中需要定義我們所需要管理的數(shù)組、對(duì)象、字符串等等,只有在這里定義了,在 vue 的組件中才能獲取你定義的這個(gè)對(duì)象的狀態(tài)。

          • 修改 state 中數(shù)據(jù)必須通過 mutations

          • 每一個(gè)可能發(fā)生改變的 state 必須同步創(chuàng)建一條或多條用來改變它的 mutations

          • 服務(wù)端獲取的數(shù)據(jù)存放在 state 中,作為原始數(shù)據(jù)保留,不可變動(dòng)。

          Getters (opens new window)有點(diǎn)類似 vue.js 的計(jì)算屬性,當(dāng)我們需要從 store 的 state 中派生出一些狀態(tài),那么我們就需要使用 getters,getters 會(huì)接收 state 作為第一個(gè)參數(shù),而且 getters 的返回值會(huì)根據(jù)它的依賴被緩存起來,只有 getters 中的依賴值(state 中的某個(gè)需要派生狀態(tài)的值)發(fā)生改變的時(shí)候才會(huì)被重新計(jì)算。

          • 通過 getters 處理你需要得到的數(shù)據(jù)格式,而不是通過修改 state 原始數(shù)據(jù)。

          • 組件內(nèi)不強(qiáng)制使用 mapGetters,因?yàn)槟憧赡苄枰褂?gettersetter

          • 改變 state 的唯一方法就是提交 mutations (opens new window)。

          • 組件內(nèi)使用 mapMutations 輔助函數(shù)將組件中的 methods 映射為 store.commit 調(diào)用。

          • 命名采用 大寫字母 + 下劃線 的規(guī)則。

          • 定義 CLEAR,以確保路由切換時(shí)可以初始化數(shù)據(jù)。

          Actions

          • 頁面重的數(shù)據(jù)接口盡量在 actions (opens new window)中調(diào)用。

          • 服務(wù)端返回的數(shù)據(jù)盡量不作處理,保留原始數(shù)據(jù)。

          • 獲取到的數(shù)據(jù)必須通過調(diào)用 mutations 改變 state

          Modules

          • 通常情況下按照頁面劃分 modules (opens new window)。

          • 默認(rèn)內(nèi)置了 system 保證了腳手架的基礎(chǔ)功能。

          • 每個(gè)頁面模塊或頁面的子模塊添加屬性 namespaced: true

          14.完成詳細(xì)的使用文檔

          不論是功能還是組件庫等等的工具,都需要完善的文檔提供查閱,即使是輪子的構(gòu)建者,也抵不住時(shí)間長了會(huì)忘記許多細(xì)節(jié)。

          這里我使用 vuepress 構(gòu)建文檔,方便快捷。

          參考【拯救懶癌文檔君 - VuePress + Travis CI + Github Pages 自動(dòng)線上生成文檔】


          15.Git 多人協(xié)作流程

          公司使用內(nèi)部搭建的 GitLab 托管代碼

          Root 倉庫

          項(xiàng)目啟動(dòng)時(shí),由項(xiàng)目管理者搭建起最原始的倉庫,稱為 Root 倉庫(源倉庫)。

          源倉庫的有個(gè)作用 :

          • 匯總參與該項(xiàng)目的各個(gè)開發(fā)者的代碼。

          • 存放趨于穩(wěn)定和可發(fā)布的代碼。

          • 向 Master 分支提交 Merge Requests 可以觸發(fā)測(cè)試環(huán)境構(gòu)建(CI/CD)。

          • 源倉庫是受保護(hù)的,開發(fā)者不可直接對(duì)其進(jìn)行開發(fā)工作。


          開發(fā)者倉庫

          任何開發(fā)者都沒有權(quán)限對(duì) Root 倉庫進(jìn)行直接的操作,源倉庫建立以后,每個(gè)開發(fā)者需要做的事情就是把源倉庫的 Fork 一份,作為自己日常開發(fā)的倉庫。

          • 每個(gè)開發(fā)者所Fork的倉庫是完全獨(dú)立的,互不干擾。

          • 每個(gè)開發(fā)者提交到代碼到自己的倉庫中,開發(fā)工作完成以后,開發(fā)者可以向源倉庫發(fā)送 Pull Request ,本地倉庫先合并源倉庫,解決沖突。

          • 發(fā)起 Merge Request 請(qǐng)求管理員把自己的代碼合并到源倉庫中的 master 或 其他分支。


          Git 流程

          • 前端項(xiàng)目會(huì)在 Root 倉庫下創(chuàng)建 dev 分支,用于代碼的拉取和合并,如果有多個(gè)不同的測(cè)試環(huán)境,按照測(cè)試環(huán)境創(chuàng)建分支。

          • 在本地的倉庫中創(chuàng)建你的 dev 分支和其他功能性的分支。

          • 開發(fā)過程中不允許直接在 master 分支上開發(fā),創(chuàng)建一個(gè)新的分支進(jìn)行開發(fā),git checkout –b {branch_name}

          • 規(guī)范且詳細(xì)的書寫 commit ,推薦使用 git-cz 工具進(jìn)行提交。

          • 完成開發(fā)后將相應(yīng)的分支合并到自己倉庫的 master 分支。

          • master 分支 push 到自己的遠(yuǎn)程倉庫(Fork倉庫)。

          • Root 倉庫 dev 分支提交 Merge Requests

          • 提醒前端負(fù)責(zé)人審查代碼、解決沖突或測(cè)試環(huán)境上線。

          • 解決沖突后 git pull upstream dev 拉取解決后的最新代碼。

          瀏覽 33
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产一级无码Av片在线观看 | 探花无码| 亚洲美女日逼 | 婷婷激情丁香五月天 | 骚逼成人av |