Vuejs技術(shù)棧從CLI到打包上線實(shí)戰(zhàn)全解析
閱讀目錄
前言
本文是自己學(xué)習(xí)vue項(xiàng)目實(shí)踐中轉(zhuǎn)載學(xué)習(xí)的一些總結(jié)角塑,針對(duì)Vue2及相關(guān)技術(shù)棧锯七,實(shí)踐中版本為2.3.3牡属。
開發(fā)前須知
vue-cli
在開發(fā)前咐鹤,我們要至少通讀一遍vue官方文檔和API(看官方文檔是最重要的,勝過看五十、一百篇博客),英文閱讀能力還行的建議閱讀英文文檔缰冤,中文文檔內(nèi)容會(huì)稍落后,還要通讀相關(guān)的vue-router喳魏、axios锋谐、vuex等。
一般來說我們都是先利用vue-cli來搭建項(xiàng)目基本架構(gòu)截酷。
vue-cli官方temaplte地址涮拗,我們選擇webpack版本,建議看看其文檔vue-webpack-boilerplate了解基本用法和項(xiàng)目配置等迂苛。
深入地了解vue-cli的webpack配置請(qǐng)查看vue-cli#2.0 webpack 配置分析
打造團(tuán)隊(duì)的腳手架
vue-cli雖然強(qiáng)大三热,但是它有很多個(gè)步驟要我們?nèi)ミx擇配置,而實(shí)際上公司業(yè)務(wù)很多配置是固定的三幻,比如我們公司規(guī)定了要安裝vue-router就漾、要使用Standard風(fēng)格Eslint等,還規(guī)定了必須使用sass念搬,這樣在vue-cli配置完成后還必須要npm install node-sass和sass-loader抑堡,還有axios等也是一定要安裝的。所以不應(yīng)該每次新建一個(gè)項(xiàng)目都去一步步選擇vue-cli的那些配置然后還要去安裝sass等朗徊,應(yīng)該在vue-cli基礎(chǔ)上根據(jù)公司自身的情況打造團(tuán)隊(duì)的腳手架首妖,只需運(yùn)行腳手架,就可以初始化整個(gè)項(xiàng)目爷恳。
目錄結(jié)構(gòu)
建議在src/目錄增加views或pages目錄來存放對(duì)應(yīng)路由的組件有缆,添加api目錄,根據(jù)項(xiàng)目情況增加filters、vuex等目錄棚壁。components目錄存放公共組件或者全局組件杯矩。每個(gè)組件目錄可以將圖片等資源放在一起。組件的子組件目錄建議命名為children放在父組件目錄下袖外。如home組件目錄為home/home.vue史隆,子組件banner路徑為home/chldren/banner/banner.vue。
靜態(tài)資源處理
vue-webpack-boilerplate文檔中有靜態(tài)資源處理的詳細(xì)說明曼验,但發(fā)現(xiàn)還有很多人都不知道逆害,因此在這里稍微提一下。
vue-webpack-boilerplate的項(xiàng)目結(jié)構(gòu)中蚣驼,我們有靜態(tài)資源兩個(gè)目錄:src/assets和static/
assets目錄中的文件會(huì)被webpack處理,只支持相對(duì)路徑形式相艇,assets/logo.png會(huì)被編譯為./assets/logo.png颖杏,不支持/assets/logo.png
在js中,我們可以這樣獲取文件資源路徑
require('./assets/logo.png')
以下帶~前綴類似require效果
<img src="~assets/logo.png">
static目錄中的靜態(tài)資源不會(huì)被webpack處理坛芽,這里適合放一些外部不需要webpack處理的資源留储,build后的靜態(tài)資源都會(huì)被放進(jìn)這個(gè)目錄。
vue組件化
關(guān)于vue組件化咙轩,360奇舞團(tuán)前端工程師鐘恒的pptVue.js實(shí)踐 如何使用Vue2.0開發(fā)富交互式WEB應(yīng)用寫得非常好获讳,本節(jié)內(nèi)容也是出自其中。ppt中提到組件化帶來的新問題:通信活喊、復(fù)用丐膝、耦合,以及如何解決钾菊。
通信
1)props和events:props down帅矗,events up
2)函數(shù)調(diào)用:this.refs
3)組件樹: parent
4)共享state
5)eventbus
6)vue技術(shù)棧之外的如localstorage等
復(fù)用
1)冗余:if、else if煞烫、else判斷執(zhí)行不同的代碼
if(this.type === 'editing') {
// some editing code
} else if(this.type === 'preview') {
// some preview code
} else if(this.type === 'present') {
// some present code
} else {
// some base code
}
2)包裝:slots
// plugin-page.vue
<div>
<slot name="page">
i am a page
</slot>
</div>
// present-plugin-page.vue
<div class="PresetPluginPage">
<plugin-page ref="page">
<h1 slot="page">
i am a present page
</h1>
</plugin-page>
</div>
//output
<div class="PresetPluginPage">
<div>
<h1>
i am a present page
</h1>
</div>
</div>
3)繼承:mixins
// define a mixin object
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// define a component that uses this mixin
var Component = Vue.extend({
mixins: [myMixin]
})
var component = new Component()
// -> "hello from mixin!"
組件耦合
1)組件耦合帶來的問題:
- 單組件修改困難
- 組合新組件困難
- 組件debug困難
2)解耦
解耦的本質(zhì)就是將變化分離
一浑此、組件功能單一
// wrong
<control-input type="number"></control-input>
// right
<control-number></control-number>
二、采用穩(wěn)定的接口
// wrong
this.$parent.$parent.$refs['resource-image'].open()
// right
bus.$emit('open-resource-image')
三滞详、處理好共享的部分
bindEvents (remove) {
let method = remove
? 'removeEventListener'
: 'addEventListener'
window[method]('resize', this.handleResize)
}
3)與服務(wù)端解耦
this.$http.get('/user/detail')
.then(({body}) => {
this.user = JSON.parse(body).data
}, err => {
console.error(err)
})
user.detail().then(detail => this.detail = detail)
服務(wù)端與前端體系不一
同步異步轉(zhuǎn)換
多服務(wù)端/跨域的代碼
統(tǒng)一的錯(cuò)誤處理代碼
vuex使用中的一些注意事項(xiàng)
1)不要濫用vuex
使用Vuex并不意味著你需要將所有的狀態(tài)放入Vuex凛俱。雖然將所有的狀態(tài)放到Vuex會(huì)使?fàn)顟B(tài)變化更顯式和易調(diào)試,但也會(huì)使代碼變得冗長和不直觀料饥。如果有些狀態(tài)嚴(yán)格屬于單個(gè)組件蒲犬,最好還是作為組件的局部狀態(tài)。你應(yīng)該根據(jù)你的應(yīng)用開發(fā)需要進(jìn)行權(quán)衡和確定岸啡。
2)最好在根實(shí)例中注冊(cè)store選項(xiàng)暖哨,該store實(shí)例會(huì)注入到根組件下的所有子組件中,子組件可以通過this.$store訪問,當(dāng)狀態(tài)較多時(shí)使用建議mapState輔助函數(shù)篇裁。
3)polyfill
本次項(xiàng)目中使用了vuex沛慢,因此為兼容IE9等低版本,須引入promise的polyfill--es6-promise达布。npm install后在main.js:
import 'es6-promise/auto'
開發(fā)中的常見問題
引入axios
為了和后端進(jìn)行數(shù)據(jù)交互团甲,我們一般引入axios庫。在main.js中如下將其加入vue的原型中黍聂,這樣可以在組件中通過this.$http來獲取axios:
Object.defineProperty(Vue.prototype, '$http', { value: axios })
// 或者 Object.defineProperty(Vue.prototype, '$axios', { value: axios })
這次實(shí)踐中未采用這種做法躺苦,而是創(chuàng)建了一個(gè)getData.js進(jìn)行了統(tǒng)一管理:
import axios from 'axios'
const getSomething = (param1,param2) => axios.get(url产还,{
params: {
param1: param1,
param2: param2
}
})
export {
getSomething
}
在單文件組件中import getSomething方法再進(jìn)行調(diào)用即可匹厘。
引入iconfont
直接在main.js中import你下載的iconfont.css即可
js中判斷環(huán)境
常見的需求是開發(fā)環(huán)境須console,而線上環(huán)境不可以console脐区。默認(rèn)環(huán)境有'development'愈诚、'production'、'testing'三種牛隅。
if (process.env.NODE_ENV !== 'production') {
console.log(data)
}
設(shè)置數(shù)據(jù)模擬請(qǐng)求Mock
數(shù)據(jù)模擬請(qǐng)求利用了mock.js,配置文檔炕柔,不過這個(gè)只是簡單的數(shù)據(jù)模擬,沒有生成文檔的功媒佣,更全面的文檔匕累、Mock.js、可視化默伍、Rest欢嘿、接口過渡、文檔修改提醒也糊、支持本地部署等功能可以使用阿里RAP际插。
npm install mockjs安裝后,可在/src/api目錄下新建data.js显设,引入mockjs框弛,后可在程序入口或api入口根據(jù)開發(fā)環(huán)境來引入data.js,下面是幾個(gè)示例:
import Mock from 'mockjs'
let data = Mock.mock({
'list|1-10': [{
'id|+1': 1
}]
}) // mock一個(gè)數(shù)據(jù)
console.log(JSON.stringify(data, null, 4))
import Mock from 'mockjs'
Mock.setup({ timeout: '300‐500' })
Mock.mock(/\/login/, { code: 0 }) // 攔截login請(qǐng)求捕捂,返回對(duì)象{ code: 0 }
import Mock from 'mockjs'
Mock.setup({ timeout: '300‐500' })
Mock.mock(sitemap.cms.banners, {
results: []
}) // 攔截sitemap中cms.banners請(qǐng)求瑟枫,返回對(duì)象{ results: [] }
seo
可以使用服務(wù)端渲染或者預(yù)渲染,預(yù)渲染webpack插件github地址指攒。
Webpack
實(shí)際項(xiàng)目中還是不可避免地要修改webpack配置慷妙,如果不知道怎么改的話就去查看webpack的配置分析去進(jìn)行修改。
配置全局變量
要設(shè)置全局變量可以在build中的webpack.base.conf.js中配置externals允悦,與module同級(jí):
externals: {
sitemap: 'sitemap'
}
然后在eslinttrc.js的module.exports添加這樣一個(gè)配置:
globals: {
'sitemap': false
}
根據(jù)環(huán)境的不同加載不同的js
在這個(gè)項(xiàng)目中要根據(jù)環(huán)境(開發(fā)環(huán)境膝擂、測試環(huán)境、生產(chǎn)環(huán)境)的不同加載不同的sitemap.js,這個(gè)sitemap.js會(huì)暴露出一個(gè)全局的sitemap變量架馋,sitemap變量是個(gè)由api地址構(gòu)成的json對(duì)象狞山。利用HtmlWebpackPlugin插件的option選項(xiàng)來實(shí)現(xiàn)。
在index.html中這樣寫:
<script src="<%= htmlWebpackPlugin.options.src %>"></script>
然后在build中的各自conf.js的HtmlWebpackPlugin設(shè)置不同的src叉寂,如在開發(fā)環(huán)境中添加src那一行:
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
src: '//dev.example.com/api/sitemap.js',
inject: true
})
配置alias(別名)
在webpack.base.conf.js萍启,vue-cli已經(jīng)默認(rèn)配置好了src目錄的別名為@,建議配置src下一級(jí)目錄的別名屏鳍,這樣能減少重復(fù)書寫也更美觀勘纯,如下添加src、pages钓瞭、components別名:
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'src': resolve('src'),
'pages': resolve('src/pages'),
'components': resolve('src/components')
}
}
圖片壓縮
可以使用webpack插件image-webpack-loader來壓縮處理圖片驳遵。
多頁面
實(shí)際就是添加多個(gè)入口js然后再修改相應(yīng)配置,網(wǎng)上資料很多山涡,一搜就知道了堤结。
eslint
我們有時(shí)候需要關(guān)閉某些代碼檢查,具體配置參見Configuring ESLint - ESLint中文佳鳖,下面是常見的兩個(gè):
1)關(guān)閉eslint
/* eslint-disable */
alert('foo')
/* eslint-enable */
2)關(guān)閉禁止new
/* eslint-disable no-new */
優(yōu)化和其他
優(yōu)化
1)由于vue的追蹤對(duì)象變化原理基于使用Object.defineProperty,在處理大量數(shù)據(jù)并且不需要追蹤對(duì)象變化時(shí)媒惕,可通過Object.freeze(data)凍結(jié)對(duì)象達(dá)到優(yōu)化數(shù)據(jù)渲染處理
2)vue-router路由懶加載系吩。當(dāng)打包構(gòu)建應(yīng)用時(shí),javascript包會(huì)變得非常大妒蔚,影響頁面加載穿挨。如果我們能把不同路由對(duì)應(yīng)的組件分割成不同的代碼塊,然后當(dāng)路由被訪問的時(shí)候才加載對(duì)應(yīng)組件肴盏,這樣就更加高效了科盛。
其他
1)使用表驅(qū)動(dòng)法來注冊(cè)全局filter、指令等菜皂,如在src下新建filters目錄贞绵,index.js中import所有全局過濾器:
import milliFormat from './milliFormat'
import reverse from './reverse'
export default {
milliFormat,
reverse
}
然后在main.js中注冊(cè)
import commonFiltes from './filters/index'
Object.keys(commonFiltes).forEach(key => Vue.filter(key, commonFiltes[key]))
2)對(duì)于一些強(qiáng)耦合的組件如collapse和collapse-item,可以使用children來進(jìn)行通信恍飘,沒必要像elementUI一樣自己實(shí)現(xiàn)組件的broadcast和dispatch榨崩,我還發(fā)現(xiàn)有UI庫竟然是使用bus來通信的,這樣導(dǎo)致同一個(gè)頁面要是有兩個(gè)collapse章母,就會(huì)互相影響母蛛。
3)在根組件上注冊(cè)公共過濾器后,除了在“Mustache”語法中使用乳怎,還可在組件中通過this.options.filters.datetime(data)獲取datetime過濾器彩郊。
打包上線
優(yōu)化分析
npm run build --report進(jìn)行打包大小分析,可視化地看到有什么地方需要優(yōu)化。
測試build后的文件
build成功后有個(gè)tip提示你build后的文件需要部署在http服務(wù)器上秫逝,不能通過file協(xié)議打開恕出。
我們可以通過node-static來啟動(dòng)服務(wù)】甑牵可以寫一個(gè)js配置文件通過node來啟動(dòng)剃根,或者CLI中輸入static dist(先安裝node-static):
$ static dist
serving "dist" at http://127.0.0.1:8080
更多如設(shè)置端口等請(qǐng)點(diǎn)擊上面的鏈接查看文檔。
后語
本文最重要的是文章中給出的一些鏈接,尤其是開發(fā)前須知章節(jié)中的鏈接,最好點(diǎn)進(jìn)去通讀一下妓美。