<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開發(fā)實(shí)戰(zhàn)-教程篇

          共 39612字,需瀏覽 80分鐘

           ·

          2022-03-18 10:37

          本文適合對(duì)vue開發(fā)過程有疑惑,以及對(duì)vue實(shí)際開發(fā)過程感興趣的小伙伴閱讀。

          如果這篇文章有觸動(dòng)到你,歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進(jìn)階~

          作者:廣東靚仔

          一、前言

          本文基于開源項(xiàng)目:

          https://github1s.com/vuejs/vue

          https://vuejs.org/

              最近有不少小伙伴私聊廣東靚仔,能不能出一期關(guān)于vue日常項(xiàng)目開發(fā)的文章,廣東靚仔整理了以往開發(fā)過的vue項(xiàng)目,結(jié)合目前業(yè)界主流方案,因此有了這篇文章。

          目錄:
          • 自定義Webpack和Babel配置
          • 設(shè)計(jì)一個(gè)高擴(kuò)展性的路由
          • 可動(dòng)態(tài)改變的頁面布局
          • 將菜單和路由結(jié)合
          • 精細(xì)化的權(quán)限設(shè)計(jì)
          • 使用其他第三方庫
          • 使用Mock數(shù)據(jù)進(jìn)行開發(fā)
          • 引入Axios
          • 管理系統(tǒng)中使用的圖標(biāo)
          • 定制主題及動(dòng)態(tài)切換主題
          • 國際化
          • 構(gòu)建打包發(fā)布
          • 做好組件的單元測(cè)試

          二、現(xiàn)有方案

              目前業(yè)界有很多現(xiàn)成的解決方案,廣東靚仔列舉了幾個(gè):

          Ant Design Pro

          D2 Admin截圖

          soybean-admin截圖

          以上都是比較穩(wěn)定的管理系統(tǒng)解決方案,有興趣的小伙伴可以去看看~下面我們一起來梳理梳理,vue實(shí)際開發(fā)的一些需要考慮到的點(diǎn)。

          三、正文

              在使用vue開發(fā)我們項(xiàng)目的時(shí)候,一般的都是采用現(xiàn)有的開源項(xiàng)目,然后進(jìn)行一些改造(二次開發(fā)),來滿足我們的業(yè)務(wù)需求,這種方案效率是最高的,成本也是最低的。

          下面開始講講vue實(shí)際項(xiàng)目開發(fā)需要關(guān)注的模塊,具體內(nèi)容如下所示:

          使用Vue CLI 3快速創(chuàng)建項(xiàng)目

          腳手架,不是本文要講的重點(diǎn),隨便看看即可~

          全局安裝

           npm install -g @vue/cli 
           or
           yarn global add @vue/cli

          新建項(xiàng)目

          vue create my-project

          最后啟動(dòng)項(xiàng)目,看到如下效果:

          自定義Webpack和Babel配置

          webpack.config.js代碼如下:

          let path = require('path');
          let webpack = require('webpack');
          /*
           html-webpack-plugin插件,webpack中生成HTML的插件,
           具體可以去這里查看https://www.npmjs.com/package/html-webpack-plugin
           */

          let HtmlWebpackPlugin = require('html-webpack-plugin');
          /*
           webpack插件,提取公共模塊
           */

          let CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
          let config = {
            //入口文件配置
            entry: {
              index: path.resolve(__dirname, 'src/js/page/index.js'),
              vendors: ['vue''vue-router','vue-resource','vuex','element-ui','element-ui/lib/theme-default/index.css'// 需要進(jìn)行單獨(dú)打包的文件
            },
            //出口文件配置
            output: {
              path: path.join(__dirname, 'dist'), //輸出目錄的配置,模板、樣式、腳本、圖片等資源的路徑配置都相對(duì)于它
              publicPath'/dist/',                //模板、樣式、腳本、圖片等資源對(duì)應(yīng)的server上的路徑
              filename'js/[name].js',            //每個(gè)頁面對(duì)應(yīng)的主js的生成配置
              chunkFilename'js/[name].asyncChunk.js?'+new Date().getTime() //chunk生成的配置
            },
            module: {
              //加載器
              rules: [
                {
                  test/\.vue$/,
                  loader'vue-loader',
                  options: {
                    loaders: {
                      scss'vue-style-loader!css-loader!sass-loader'// <style lang="scss">
                      sass'vue-style-loader!css-loader!sass-loader?indentedSyntax' // <style lang="sass">
                    }
                  }
                },
                {
                  test/\.html$/,
                  loader"raw-loader"
                },
                {
                  test/\.css$/,
                  loader'style-loader!css-loader'
                },
                {
                  test/\.js$/,
                  exclude/node_modules/,
                  loader"babel-loader",
                  options: {
                    presets: ["es2015","stage-0"],
                    plugins: ['syntax-dynamic-import']
                  }
                },
                {
                  test/\.scss$/,
                  loader'style-loader!css-loader!sass-loader'
                },
                {
                  test/\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/,
                  loader'file-loader'
                },
                {
                  //圖片加載器,雷同file-loader,更適合圖片,可以將較小的圖片轉(zhuǎn)成base64,減少http請(qǐng)求
                  //如下配置,將小于8192byte的圖片轉(zhuǎn)成base64碼

                  test/\.(png|jpg|gif)$/,
                  loader'url-loader?limit=8192&name=images/[hash].[ext]'
                }
              ]
            },
            //插件
            plugins: [
              //webpack3.0的范圍提升
              new webpack.optimize.ModuleConcatenationPlugin(),
              //打包生成html文件,并且將js文件引入進(jìn)來
              new HtmlWebpackPlugin({
                filename: path.resolve(__dirname, 'dist/html/index.html'), //生成的html存放路徑,相對(duì)于path
                template: path.resolve(__dirname, 'src/html/index.html'), //ejs模板路徑,前面最好加上loader用于處理
                inject'body',  //js插入的位置,true/'head'/'body'/false
                hashtrue
              }),
              //提取功能模塊
              new CommonsChunkPlugin({
                name'vendors'// 將公共模塊提取,生成名為`vendors`的chunk
                minChunks2//公共模塊被使用的最小次數(shù)。配置為2,也就是同一個(gè)模塊只有被2個(gè)以外的頁面同時(shí)引用時(shí)才會(huì)被提取出來作為common chunks
                // children:true  //如果為true,那么公共組件的所有子依賴都將被選擇進(jìn)來
              }),
            ],
            //使用webpack-dev-server,啟動(dòng)熱刷新插件
            devServer: {
              contentBase: path.join(__dirname, "/"),
              host'localhost',  //建議寫IP地址,開發(fā)時(shí)候電腦的ip地址。localhost我不知道是幻覺還是怎樣,有時(shí)候熱刷新不靈敏
              port9090//默認(rèn)9090
              inlinetrue, //可以監(jiān)控js變化
              hottrue//熱啟動(dòng)
            },
            //搜索路徑變量
            resolve: {
              alias: {
                vue'vue/dist/vue.js'
              },
              extensions:['.js','.scss','.vue','.json']// 可以不加后綴, 直接使用 import xx from 'xx' 的語法
            }
          };
           
          module.exports = config;

          設(shè)計(jì)一個(gè)高擴(kuò)展性的路由

          根據(jù)頁面展示結(jié)構(gòu)進(jìn)行抽象,結(jié)合業(yè)務(wù)模塊進(jìn)行的合理層級(jí)劃分

          router.js代碼如下:

          import Vue from 'vue';
          import Router from 'vue-router'
          import NProgress from 'nprogress'
          import 'nprogress/nprogress.css'

          import NotFound from '../views/404';

          Vue.use(Router);

          const router =  new Router({
           mode'history',
           routes: [
            {path:'/',redirect'/user/login'},
            {
            path'/user',
            component: {renderh=>h("router-view")},
            children: [{
              path'login',
              name'index',
              component() =>
               import/* webpackChunkName: "user" */ '../views/User/Login')
             },
             {
              path'register',
              name'news',
              component() =>
               import/* webpackChunkName: "user" */ '../views/User/Register')
             },
             {
              path'*',
              name'404',
              component:NotFound
             }
            ]
           }]
          })

          router.beforeEach((to,form,next)=>{
           NProgress.start();
           next();
          });

          router.afterEach(() => {
           NProgress.done();
          });
          export default router

          可動(dòng)態(tài)改變的頁面布局

          方案一:

          定義好數(shù)據(jù)格式,一個(gè)頁面可以把它劃分成多個(gè)組件來構(gòu)成,例如一個(gè)基本的布局:

          header,main,footer。那么就可以劃分成三個(gè)組件,為這三個(gè)組件添加樣式,屬性,事件。

          {
             header:{
                style:{},
                property:{},
                event:{}
             },
             main:{
                style:{},
                property:{},
                event:{}
             }
          }

          當(dāng)數(shù)據(jù)添加進(jìn)去,生成的頁面就應(yīng)該根據(jù)這些數(shù)據(jù)來渲染

          方案二:

          定義模板,根據(jù)需要切換

          var hdInput = {
              template"<div><input/></div>"
          };
          var hdTextarea = {
              template"<div><textarea></textarea></div>"
          };
          new Vue({
              el"#hdcms",
              components: {hdInput,hdTextarea},
              data:{
                  formType:"hdInput"
              }
          });

          將菜單和路由結(jié)合

          具體方案:

          1.前端在本地寫好路由表,以及每個(gè)路由對(duì)應(yīng)的角色,也就是哪些角色可以看到這個(gè)菜單/路由;
          2.登錄的時(shí)候,向后端請(qǐng)求得到登錄用戶的角色(管理者、普通用戶);
          3.利用路由攔截,根據(jù)取到的用戶角色,跟本地的路由表進(jìn)行對(duì)比,過濾出用戶對(duì)應(yīng)的路由,并利用路由進(jìn)行左側(cè)菜單渲染

          一、本地寫好路由表

          router/index.js

          //代碼位置:router/index.js
          {
            path'',
            component: layout, //整體頁面的布局(包含左側(cè)菜單跟主內(nèi)容區(qū)域)
            children: [{
              path'main',
              component: main,
              meta: {
                title'首頁'//菜單名稱
                roles: ['user''admin'], //當(dāng)前菜單哪些角色可以看到
                icon'el-icon-info' //菜單左側(cè)的icon圖標(biāo)
              }
            }]
          }

          二、用戶登錄,獲取用戶的角色

          獲取到用戶角色,存放進(jìn)localStorage,然后跳轉(zhuǎn)主頁

          //代碼位置:src/components/reLoad.vue

          // axios.post('/temp',this.formModel).then(res=>{})      
          // 我暫時(shí)就不模擬了,直接取

          let getUserRole = this.formModel.user === 'admin' ? 'admin' : 'user'
          localStorage.setItem('userRole', getUserRole)
          this.$router.push({
            path'/main'
          })

          三、路由攔截beforeEach,并過濾出角色對(duì)應(yīng)的路由表

          關(guān)鍵技術(shù)點(diǎn)addRoutes

          //代碼位置:src/permission.js

          router.beforeEach((to, from, next) => {
            // 取到用戶的角色
            let GetRole = localStorage.getItem("userRole")
            // 如果登錄了
            if (GetRole !== 'unload') {
              next() //next()方法后的代碼也會(huì)執(zhí)行
              // 1.如果路由表 沒根據(jù)角色進(jìn)行篩選,就篩選一次
              if (!addRouFlag) {
                addRouFlag = true
                // 2.根據(jù)用戶的角色、和需要?jiǎng)討B(tài)展示的路由,生成符合用戶角色的路由
                var getRoutes = baseRoleGetRouters(permissionRouter, GetRole.split(","))
                // 3.利用global屬性,讓渲染菜單的組件sideMeuns.vue重新生成左側(cè)菜單
                global.antRouter = fixedRouter.concat(getRoutes)
                // 4.將生成好的路由addRoutes
                router.addRoutes(fixedRouter.concat(getRoutes))
                // 5.push之后,會(huì)重新進(jìn)入到beforeEach的鉤子里,直接進(jìn)入第一個(gè)if判斷
                router.push({ path: to.path })
              }
            } else {
              // 用戶沒登錄,跳轉(zhuǎn)到登錄頁面
              if (to.path === '/') {
                next()
              } else {
                next('/')
              }
            }
          })

          精細(xì)化的權(quán)限設(shè)計(jì)

          權(quán)限控制是后臺(tái)管理系統(tǒng)比較常見的需求,我們需要對(duì)某些頁面的添加權(quán)限控制,可以在路由管理中的權(quán)限做一些校驗(yàn)。

          一、權(quán)限校驗(yàn)函數(shù)

          getCurrentAuthority()函數(shù)用于獲取當(dāng)前用戶權(quán)限,一般來源于后臺(tái)數(shù)據(jù)
          check()函數(shù)用于權(quán)限的校驗(yàn)匹配
          isLogin()函數(shù)用于檢驗(yàn)用戶是否登錄
          /**
          *權(quán)限校驗(yàn)函數(shù)
          * /src/utils,/auth.js演示使用路由管理用戶權(quán)限
          **/

          // 獲取當(dāng)前用戶權(quán)限
          export function getCurrentAuthority(){
            return ["user"];
          }
          //權(quán)限校驗(yàn)
          export function check(authority){
            const current getCurrentAuthority();
            return current.some(item =authority.includes(item));
          }
          //登錄檢驗(yàn)
          export function isLogin(){
            const current getcurrentAuthority();
            return current &current[0]!="guest";
          }

          二、路由配置元信息

          路由配置元信息meta:{ authority: ["admin"] }

          /**
          * 路由配置元信息
          * /src/router/index.js
          */

          const routes =
          // 省略部分代碼
          {
            path:"/"
            meta:authority:["user","admin"]}
            component:()=
            import(/*webpackChunkName:"Layout"*/"../layouts/BasicLayout")
            //省略部分代碼
          },
          {
            path:"/403",
            name:"403",
            hideInMenu:true,
            component:()=
            import(/*webpackChunkName:"exception"*/"@/views/Exception/403")
          }
          ];

          三、路由守衛(wèi)router.beforeEach中判斷

          /**登出于形到物權(quán)限
          * /src/router/index.js
          */

          import findLast from "lodash/findLast";
          import {check,isLogin} from "../utils/auth";
          import {notification} from "ant-design-vue";
          // 路由守衛(wèi)判斷權(quán)限
          router.beforeEach((to,from,next)=>{
            if (to.path I==from.path){
              NProgress.start()
            }
            const record findLast(to.matched,record => record.meta.authority);
            if (record && !check(record.meta.authority)){
              if (lisLogin()&&to.path !=="/user/login"){
                next({
                  path:"/user/login"
                })
              } else if(to.path1 !== "/403"){
                notification.error({
                  message:"403",
                  description:"你設(shè)有訪間權(quán)限,請(qǐng)聯(lián)系管理員"
                })
                next({
                  path:"/403"
                })
             }
             NProgress.done();
          }
          next();
          })

          使用ECharts、Antv等其他第三方庫

          根據(jù)項(xiàng)目要求,按需引入

          使用第三方的開源庫,可以提高效率~

          使用Mock數(shù)據(jù)進(jìn)行開發(fā)

          一、安裝:

          npm i mockjs -D 

          -D: 僅僅在開發(fā)環(huán)境上使用

          二、項(xiàng)目引入:

          在 main.js 文件中引入mock:import '@/mock'

          三、創(chuàng)建mock文件夾

          // 引入隨機(jī)函數(shù)
          import { Random } from 'mockjs'
          // 引入Mock
          const Mock = require('mockjs')

          const userListData = Mock.mock({
              'data|10': [
                  {
                      id() => Random.id(),
                      nickName() => Random.cword('零一二三四五六七八九十'3),
                      phone() => Random.integer(1111111111199999999999),
                      tgCount() => Random.integer(0200),
                      earnings() => Random.float(20001000002),
                  },
              ],
          })

          function userList(res{
              return {
                  code200,
                  data: userListData.data,
                  message'獲取成功',
                  total20,
                  size10,
                  user_count20,
                  shop_count20,
              }
          }

          const shopListData = Mock.mock({
              'data|10': [
                  {
                      shop_id() => Random.id(),
                      shop_name() => Random.cword('零一二三四五六七八九十'3),
                      address() => Random.city(true),
                      shop_tel() => Random.integer(1111111111199999999999),
                      open_date() => Random.date(),
                      earnings() => Random.float(20001000002),
                  },
              ],
          })
          function shopList(res{
              return {
                  code200,
                  data: shopListData.data,
                  message'獲取推廣店鋪成功',
                  total20,
                  size10,
                  earnings_count20000,
                  shopCount20,
              }
          }
          export default {
              userList,
              shopList,
          }

          四、定義訪問的方法,接口,請(qǐng)求方式,請(qǐng)求參數(shù)

          import http from '../../plugins/http'

          export const getUserList = (params) => {
              return http.get('/api/cuz/userList')
          }

          export const getShopListById = (id) => {
              return http.get(`/api/cuz/shopList/${id}`)
          }

          五、攔截匹配在api中定義的請(qǐng)求,并對(duì)此返回模擬出的假數(shù)據(jù)

          // 引入mockjs
          import Mock from 'mockjs'
          // 引入模板函數(shù)類
          import ratings from './modules/ratings'
          import cuz from './modules/cuz'

          // Mock函數(shù)
          const { mock } = Mock

          // 設(shè)置延時(shí)
          Mock.setup({
              timeout400,
          })

          // 使用攔截規(guī)則攔截命中的請(qǐng)求,mock(url, post/get, 返回的數(shù)據(jù));
          mock(/\/api\/ratings\/list/'post', ratings.list)

          mock(/\/api\/cuz\/userList/'get', cuz.userList)
          mock(/\/api\/cuz\/shopList/'get', cuz.shopList)

          Axios

          一、安裝

          npm install vue-axios --save

          二、main.js引入

          import axios from 'axios'
          Vue.prototype.$axios = axios    //全局注冊(cè),使用方法為:this.$axios

          三、使用

          <script>
          export default{
            data(){
              return{
                userId:666,
                token:'',
              }
            },
            created(){
              this.$axios({
                method:'post',
                url:'api',
                data:this.qs.stringify({    //這里是發(fā)送給后臺(tái)的數(shù)據(jù)
                      userId:this.userId,
                      token:this.token,
                })
              }).then((response) =>{          //這里使用了ES6的語法
                  console.log(response)       //請(qǐng)求成功返回的數(shù)據(jù)
              }).catch((error) =>
                  console.log(error)       //請(qǐng)求失敗返回的數(shù)據(jù)
              })
            }
          }
          </script>

          四、請(qǐng)求攔截器

          // http request 攔截器
          instance.interceptors.request.use(
            config => {
              const token = sessionStorage.getItem('token')
              if (token ) { // 判斷是否存在token,如果存在的話,則每個(gè)http header都加上token
                config.headers.authorization = token  //請(qǐng)求頭加上token
              }
              return config
            },
            err => {
              return Promise.reject(err)
            })

          五、響應(yīng)攔截器

          // http response 攔截器
          instance.interceptors.response.use(
            response => {
              //攔截響應(yīng),做統(tǒng)一處理 
              if (response.data.code) {
                switch (response.data.code) {
                  case 1002:
                    store.state.isLogin = false
                    router.replace({
                      path'login',
                      query: {
                        redirect: router.currentRoute.fullPath
                      }
                    })
                }
              }
              return response
            },
            //接口錯(cuò)誤狀態(tài)處理,也就是說無響應(yīng)時(shí)的處理
            error => {
              return Promise.reject(error.response.status) // 返回接口返回的錯(cuò)誤信息
            })

          六、在需要的頁面導(dǎo)入就可以使用了

          import instance from './axios'

          /* 驗(yàn)證登陸 */
          export function handleLogin (data{
            return instance.post('/ds/user/login', data)
          }

          管理系統(tǒng)中使用的圖標(biāo)

          項(xiàng)目中的圖標(biāo)需要集中管理起來,方便維護(hù),減少一些圖片重復(fù)引入

          如果對(duì)安全沒什么特殊要求:推薦使用iconfont

          如果對(duì)安全有特別要求:把圖標(biāo)統(tǒng)一存放在內(nèi)部服務(wù)

          定制主題及動(dòng)態(tài)切換主題

          結(jié)合ElementUI使用

          (Tips: 廣東靚仔看到業(yè)界關(guān)于動(dòng)態(tài)主題大約有6種方案,選了其中一種)

          修改ElementUI提供的變量,先根據(jù)實(shí)際情況修改變量值

          // 參考:https://element.eleme.cn/#/zh-CN/component/custom-theme
          /* 改變主題色變量 */
          $--color-primary: #545C64;
          $--color-success: #27B6AF;
          $--menu-background-color: #1D212A;
          $--menu-item-font-color: #B3B8C3;
          $--menu-item-hover-fill: #1F2D3D;
          $--main-padding: 15px;
          /* 改變 icon 字體路徑變量,必需 */
          $--font-path: '~element-ui/lib/theme-chalk/fonts';
          // 通用的布局等樣式
          @import "../common";

          common.scss片段:

          // 自定義變量
          $---menu--inline-background-color: #13161C !default;
          $---index-header-height: 50px !default;
          $---padding-common: 15px !default;
          $---margin-common: 15px !default;
          $---border-line-color: #E6E6E6 !default;
          @import "~element-ui/packages/theme-chalk/src/index";
          .el-menu-item.is-active {
            color: $--color-white;
            background-color: $--menu-item-hover-fill;
            font-weight: $--font-weight-primary;
          }
          // .............更多見GitHub源文件

          main.js中引入

          // 樣式配置
          import './assets/css/main.scss'

          動(dòng)態(tài)主題

          定義好模板主題文件,這里列舉了defaut、simple兩個(gè)主題

          main.scss主要內(nèi)容:

          // 實(shí)際樣式引入
          .theme-simple {
            @import "src/assets/themes/simple/index";
          }
          .theme-default {
            @import "src/assets/themes/default/index";
          }

          切換主題

          改變body的樣式名稱即可,調(diào)用$changeTheme(theme)

          const $themeList = [
            {
              id'theme-default',
              name'默認(rèn)主題'
            }, {
              id'theme-simple',
              name'簡(jiǎn)單主題'
            }
          ]    
          Vue.prototype.$changeTheme = function (theme = $themeList[0]{
              const body = document.querySelector('body')
              $themeList.forEach(t => {
                  body.classList.remove(t.id)
              })
              body.classList.add(theme.id)
              store.dispatch('Theme/changeTheme', theme) // 暫時(shí)保存到store里面
          }

          Tips: 圖標(biāo)在主題樣式顯示有點(diǎn)問題,使用font-face兼容下

          //***********這塊font定義是為了修復(fù)問題********************
          $--font-path: '~element-ui/lib/theme-chalk/fonts';
          @font-face {
            font-family: 'element-icons';
            src: url('#{$--font-path}/element-icons.woff') format('woff'), url('#{$--font-path}/element-icons.ttf') format('truetype'); 
            font-weight: normal;
            font-display: auto;
            font-style: normal;
          }

          做好國際化

          i18n

          一、 使用國際化來更改咱們的項(xiàng)目語言

          簡(jiǎn)單介紹下i18n如何用

          1. 安裝:

          //使用yarn
          yarn add vue-i18n 
          //npm
          npm i vue-i18n -S

          2. 使用:

          系統(tǒng)中使用它,必須通過 Vue.use() 明確地安裝 vue-i18n:

          src/i18n/index.js

          //src/i18n/index.js
          import Vue from 'vue'
          import VueI18n from 'vue-i18n'

          Vue.use(VueI18n)
          // 準(zhǔn)備翻譯的語言環(huán)境信息
          const messages = {
            en: { 
              message: {
                hello'hello world'
              }
            },
            ja: {
              message: {
                hello'こんにちは、世界'
              }
            }
          }

          // 通過選項(xiàng)創(chuàng)建 VueI18n 實(shí)例
          const i18n = new VueI18n({
            locale'ja'// 設(shè)置地區(qū)
            messages // 設(shè)置地區(qū)信息
          })

          3. 將i18n實(shí)例掛載在Vue的option中

          import Vue from 'vue'
          import i18n from "./src/i18n/index.js"
          new Vue({
           i18n
          })

          4. 視圖顯示

          <div id="app">
            <p>{{ $t("message.hello") }}</p>
          </div>
          ------------------------------------
          <!-- 展示效果如下 -->
          <div id="app">
           <p>hello world</
          p>
          </div>

          在插值中使用$t函數(shù)就可以了

          二、vue-cli項(xiàng)目中使用

          1. 創(chuàng)建i18n文件結(jié)構(gòu)

          目錄結(jié)構(gòu)如下:

          這里列舉了兩種語言分別是:en英文zh中文

          en.js

          export default {
            table: { // 假如用于翻譯表格
              date"Date",
              name"Name",
              address"Address"
            },
            menu: {}, // 假如項(xiàng)目中日后還有菜單
            tabs: {} // tab切換等
          }

          zh.js

          export default {
            table: {
              date"日期",
              name"姓名",
              address"地址"
            },
            menu: {},
            tabs: {}
          }

          config文件夾下面的index.js,代碼如下(二者都可以):

          乞丐版:

          import en from './config/en'
          import id from './config/id'
          import ja from './config/ja'
          import ae from './config/ae'
          import am from './config/am'
          import ca from './config/ca'
          import al from './config/al'
          .....

          至尊版:

          import Vue from "vue"
          import VueI18n from "vue-i18n"
          Vue.use(VueI18n)//注入到所有的子組件

          //require.context(path,deep,regExp)
          //有3個(gè)方法 分別是keys() 


          // 至尊版
          let langFileds = require.context('./config', false, /\.js$/)

          let regExp = /\.\/([^\.\/]+)\.([^\.]+)$/ //正則用于匹配 ./en.js中的'en'

          // regExp.exec('./en.js')

          let messages = {} //聲明一個(gè)數(shù)據(jù)模型,對(duì)應(yīng)i18n中的message屬性

          langFileds.keys().forEach(key => {
              let prop = regExp.exec(key)[1//正則匹配en|zh這樣的值
              //messages[prop]相當(dāng)于 messages['en'] = {table:{...}}
              messages[prop] = langFileds(key).default

          })
          console.log(messages);
          console.log(langFileds('./en.js'));

          let locale = localStorage.getItem('lang') || "zh" //從localstorag中獲取

          export default new VueI18n({
              locale,//指定語言字段
              messages//定義語言字段
          })

          2. 修改main.js

          import Vue from 'vue'
          import App from './App.vue'
          import ElementUI from "element-ui" // 需要安裝 element-ui
          import 'element-ui/lib/theme-chalk/index.css';
          Vue.config.productionTip = false
          Vue.use(ElementUI)

          import i18n from "./i18n" //

          new Vue({
            renderh => h(App),
            i18n // 掛載
          }).$mount('#app')

          3. 具體使用demo

          app.vue

          <template>
            <div id="app">
              <template>
                <el-table :data="tableData"
                          style="width: 100%">

                  <el-table-column prop="date"
                                   :label="$t('table.date')"
                                   width="180">

                  </el-table-column>
                  <el-table-column prop="name"
                                   :label="$t('table.name')"
                                   width="180">

                  </el-table-column>
                  <el-table-column prop="address"
                                   :label="$t('table.address')">

                  </el-table-column>
                </el-table>

              </template>
              <el-button type="primary"
                         @click="change('zh')">
          點(diǎn)擊切換中文</el-button>
              <el-button type="primary"
                         @click="change('en')">
          點(diǎn)擊切換英文</el-button>
              <el-button type="primary"
            </div>

          </template>

           <script>
            export default {
              mounted() {
                console.log(this.$i18n.t('table.date'));
              },
              methods: {
                change(lang) { //切換方法
                  localStorage.setItem('lang', lang)
                  window.location.reload() //localSotrage是不響應(yīng)的,為了演示效果所以直接調(diào)用刷新
                }
              },
              data() {
                return {
                  tableData: [{
                    date'2016-05-02',
                    name'王小虎',
                    address'上海市普陀區(qū)金沙江路 1518 弄'
                  }]
                }
              }
            }
            </script>

            <style>
            #app {
              width50%;
            }
          </style>

          構(gòu)建打包發(fā)布

          1. 打包配置如下:

          build: {
              envrequire('./prod.env'),
              index: path.resolve(__dirname, '../dist/index.html'),
              assetsRoot: path.resolve(__dirname, '../dist'),
              assetsSubDirectory'static',
              assetsPublicPath'./',
              productionSourceMaptrue,
              // 默認(rèn)情況下,Gzip 關(guān)閉了許多流行的靜態(tài)主機(jī),例如 
              // Surge 或 Netlify 已經(jīng)為您壓縮了所有靜態(tài)資產(chǎn)。 
              // 在設(shè)置為 `true` 之前,請(qǐng)確保: 
              // npm install --save-dev compression-webpack-plugin

              productionGzipfalse,
              productionGzipExtensions: ['js''css'],
              // 運(yùn)行帶有額外參數(shù)的構(gòu)建命令 
              // 構(gòu)建完成后查看包分析器報(bào)告: 
              // `npm run build --report` 
              // 設(shè)置為 `true` 或 `false` 以始終打開或關(guān)閉它

              bundleAnalyzerReport: process.env.npm_config_report
            }

          2. 一般部署,我們會(huì)結(jié)合Nginx一起使用

          安裝&啟動(dòng)

          # 安裝,安裝完成后使用nginx -v檢查,如果輸出nginx的版本信息表明安裝成功
          sudo apt-get install nginx
          # 啟動(dòng)
          sudo service nginx start

          3. 修改nginx配置

          nginx的配置文件就在/etc/nginx文件夾

          /etc/nginx/sites-available/default

          nginx代理的根目錄是/var/www/html

          mkdir /www
          echo 'Hello world' > /www/index.html

          4. 同步到服務(wù)器

          在git-bash或者powershell使用scp指令,如果是linux環(huán)境開發(fā),還可以使用rsync指令:

          scp -r dist/* [email protected]:/www

          rsync -avr --delete-after dist/* [email protected]:/www 

          package.json腳本,方便,提高效率

          "scripts": {
            "build""vue-cli-service build",
            "push""yarn build && scp -r dist/* [email protected]:/www"
          },

          當(dāng)然啦,對(duì)于history、與hash模式,對(duì)應(yīng)微調(diào)下即可~

          做好組件的單元測(cè)試

          Vue 的單文件組件使得為組件撰寫隔離的單元測(cè)試這件事更加直接

          組件的單元測(cè)試有很多好處:

          • 提供描述組件行為的文檔
          • 節(jié)省手動(dòng)測(cè)試的時(shí)間
          • 減少研發(fā)新特性時(shí)產(chǎn)生的 bug
          • 改進(jìn)設(shè)計(jì)
          • 促進(jìn)重構(gòu)

          一個(gè)簡(jiǎn)單的Demo:

          <template>
            <div>
              <div class="message">
                {{ message }}
              </div>
              Enter your username: <input v-model="username">
              <div
                v-if="error"
                class="error"
              >

                Please enter a username with at least seven letters.
              </div>
            </div>
          </template>


          <script>
          export default {
            name'Foo',

            data () {
              return {
                message'Welcome to the Vue.js cookbook',
                username''
              }
            },

            computed: {
              error () {
                return this.username.trim().length < 7
              }
            }
          }
          </script>

          單元測(cè)試,代碼如下:

          import { shallowMount } from '@vue/test-utils'
          import Foo from './Foo'

          const factory = (values = {}) => {
            return shallowMount(Foo, {
              data () {
                return {
                  ...values
                }
              }
            })
          }

          describe('Foo', () => {
            it('renders a welcome message', () => {
              const wrapper = factory()

              expect(wrapper.find('.message').text()).toEqual("Welcome to the Vue.js cookbook")
            })

            it('renders an error when username is less than 7 characters', () => {
              const wrapper = factory({ username''  })

              expect(wrapper.find('.error').exists()).toBeTruthy()
            })

            it('renders an error when username is whitespace', () => {
              const wrapper = factory({ username' '.repeat(7)  })

              expect(wrapper.find('.error').exists()).toBeTruthy()
            })

            it('does not render an error when username is 7 characters or more', () => {
              const wrapper = factory({ username'Lachlan'  })

              expect(wrapper.find('.error').exists()).toBeFalsy()
            })
          })

          Tips:   工廠函數(shù)將 values 對(duì)象合并到了 data 并返回了一個(gè)新的 wrapper 實(shí)例。好處有兩個(gè):

          1. 不需要在每個(gè)測(cè)試中重復(fù) const wrapper = shallowMount(Foo)

          2.  當(dāng)我們想為更復(fù)雜的組件在每個(gè)測(cè)試中偽造或存根一個(gè)方法或計(jì)算屬性時(shí),你只需要聲明一次即可。

          Vue Test Utils 及龐大的 JavaScript 生態(tài)系統(tǒng)提供了大量的工具促進(jìn) 100% 的測(cè)試覆蓋率。

          推薦閱讀:

          https://v1.test-utils.vuejs.org/zh/guides/#%E8%B5%B7%E6%AD%A5

          四、總結(jié)

              在我們閱讀完官方文檔后,我們一定會(huì)進(jìn)行更深層次的學(xué)習(xí),比如看下框架底層是如何運(yùn)行的,以及源碼的閱讀。
              這里廣東靚仔給下一些小建議:
          • 在看源碼前,我們先去官方文檔復(fù)習(xí)下框架設(shè)計(jì)理念、源碼分層設(shè)計(jì)
          • 閱讀下框架官方開發(fā)人員寫的相關(guān)文章
          • 借助框架的調(diào)用棧來進(jìn)行源碼的閱讀,通過這個(gè)執(zhí)行流程,我們就完整的對(duì)源碼進(jìn)行了一個(gè)初步的了解
          • 接下來再對(duì)源碼執(zhí)行過程中涉及的所有函數(shù)邏輯梳理一遍

          關(guān)注我,一起攜手進(jìn)階

          歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進(jìn)階~

          瀏覽 80
          點(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>
                  日日夜夜黄片 | 日本黄色片小视频 | 操逼 88AV| 免费视频一区二区三区四 | 色老板在线精品 |