本文首發于我的博客
前言
慢慢地也用vue2+webpack2重構了一整個項目,期間自己踩過的坑和思考記錄下來,既讓自己復盤一遍也方便以后查找相關的問題
項目構建
因為vue-cli實在是太友好了,并且也是打算做單頁應用,所以直接用vue-cli構建基于webpack2的模板。
整體技術棧使用vue2
+webpack2
+vuex
+Axios
+vue-router
項目總體結構↓
|-- build // 項目構建webpack相關代碼
|-- config // 項目開發環境配置
|-- node-moudles //node依賴包(自動生成的)
|-- src // 源碼目錄(此文件內基本就是開發者主要編寫代碼存放資源的地方)
|--router //路由文件
|--vuex //vuex文件
|--style //公共樣式文件
|--assets //靜態資源(大多都是放一些樣式類文件,如css、scss等,不知道為啥,當時我沒有用)
|--components //組件文件
|-- static // 靜態文件,比如一些圖片,json數據等(打包成生產環境時,會將這個文件直接打包進去)
|-- .babelrc // ES6語法編譯配置
|-- .editorconfig // 定義代碼格式
|-- .gitignore // git上傳需要忽略的文件格式
|-- README.md // 項目說明
|-- favicon.ico // 頁面標簽顯示的logo
|-- index.html // 入口頁面
|-- package.json // 項目基本信息
build文件夾
|-- build // 項目構建webpack相關代碼
|--build.js //生產環境構建
|--check-version.js //版本檢查
|--dev-client.js //開發服務器熱重載
|--dev-server.js //構建本地服務器
|--utils //構建相關工具
|--vue-loader.conf.js //CSS加載器配置
|--webpack.base.conf.js //webpack基礎配置
|--webpack.dev.conf.js //webpack開發環境配置
|--webpack.prod.conf.js //webpack生產環境配置
|--webpack.test.conf.js //webpack測試環境配置
build主要是用于webpack的一些相關配置,比如內部含有的啟動文件dev-server.js
.當我們運行npm run dev
命令時node就會根據
package.json
中的"dev": "node build/dev-server.js",
來尋找此文件,這些都是默認設置。無需要修改
"scripts": {
"dev": "node build/dev-server.js",
"start": "node build/dev-server.js",
"build": "node build/build.js",
"unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
"e2e": "node test/e2e/runner.js",
"test": "npm run unit && npm run e2e"
},
config文件夾
config主要就是項目的相關配置,比如自定義修改監聽端口,打包輸出的路徑及其輸出文件的命名
|-- config // 項目構建webpack相關代碼
|--dev.env.js //項目開發環境配置
|--index.js //項目主要的配置(監聽端口,打包路徑啥的都在這里寫)
|--prod.env.js //項目生產環境配置
|--test.env.js //項目測試環境配置
上述三個文件做簡單介紹是因為最后我們在打包優化主要是對于webpack的配置文件進行修改,所以先做簡單介紹。
編碼過程問題總結
路由標簽自定義
用到vue2框架的時候不可避免會用到路由插件,一般配合vue2的官方路由插件是vue-router2
其中有個路由跳轉標簽為router-link
例子如下
<router-link to="/foo">foo</router-link>
<!-- 渲染結果 -->
<a href="/foo">foo</a>
但是由于樣式的原因。有時候我們不希望他被渲染成<a href=""></a>
那我們可以利用router-link
中的tag
屬性改變渲染過后的標簽
例子如下
<router-link to="/foo" tag="li">foo</router-link>
<!-- 渲染結果 -->
<li>foo</li>
自定義路徑別名(@
,~
)
在vue2過程導入組件或者css時可能會有這種語法
import Index from '@/components/Index'
這種@
是什么東西?
在配置文件中這是webpack的配置項之一:路徑別名
我們可以在基礎配置文件中自定義自己的路徑別名,比如設置~
為src/components
:
// build/webpack.base.js
{
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'~': resolve('src/components')
}
}
}
然后導入組件就可以直接這樣寫
//import YourComponent from 'src/components/YourComponent
import YourComponent from '~/YourComponent'
這樣就解決了路徑過長與相對路徑的煩惱,方便多了吧
CSS作用域與模塊
在組件化開發過程中本著分離的原則,CSS也應該進行獨立分離,這樣更方便以后維護。
我們可以通過添加scoped
屬性來使style中的樣式只作用于當前組件
<style lang="less" scoped>
@import 'other.less';
.main{
color:#fff
}
</style>
注意:
在有scoped屬性的style標簽內導入其他樣式,同樣會被限制于該作用域,變為組件內的樣式。這樣再每次打包之后會大大增加CSS體積,所以復用程度較高的樣式不建議這樣使用
,第一次我就出了這個問題
另外,在組件內樣式中應該避免使用元素選擇器,原因在于元素選擇器與屬性選擇器進行查詢時性能會大大降低
好像這條不止于在vue中,而是css本來就存在的
v-for的使用
v-for
指令真是無比便捷,他讓我們大大減少了HTML代碼的編碼量,并且讓模板更加與數據契合。
不僅可以遍歷數組,對象,甚至可以遍歷字符串與數字
我們經常會遇到一些比如,上下移動行啊什么的需求。這些需求基本都繞不過索引
在使用v-for時候我們可以直接
<li v-for="(item,index) of items" :index="index" :key="index"></li>
這樣直接可以利用li標簽的index屬性就可以拿到當前這行的index。tr同理
但是在便利數字時候需要特別注意,數字是從1開始的
項目路徑配置
vue-cli
提供的 npm run build
打包出的dist文件是默認把index.html當做根目錄引用的,然而我們有時候是需要部署到服務中的子目錄下
那么我們可以修改config/index.js
中來改變相對路徑:
build.assetsSubDirectory: 'static'
build.assetsPublicPath: '/'
dev.assetsSubDirectory: 'static'
dev.assetsPublicPath: '/'
assetsSubDirectory
指的是靜態資源文件夾,也就是打包后的js、css、img等文件所放置的文件夾。這個一般都不需要修改
assetsPublicPath
而這個屬性就是指index中引用靜態資源的路徑,默認為/
。如果我們需要做特殊配置修改這里的路徑就好。路徑修改為最后你的index.html
到這個靜態資源的相對路徑
開發環境配置
何為開發環境:在開發時,不可避免會產生大量debug又或是測試的代碼,這些代碼不應出現在生產環境中(也即不應提供給用戶)。
開啟webpack的cache
在webpack.base.conf.js
中,在module.exports
內加入cache: true
這個屬性
配置加載器loader中的include與exclude
加載器loader
中的include
屬性代表需要編譯的文件,而反之exclude
就是不需要編譯的文件。
大多數情況下我們幾乎都不需要對node_modules
下的js
文件進行編譯,我們只需要對我們項目目錄下自己編寫的js
進行編輯就好,所以我們通過配置這兩個選項,大大提升我們的構建速度
在webpack.base.conf.js
中
// ... 其他配置
module: {
rules: [
{
test: /\.js$/,
loader: ['babel-loader?cacheDirectory=true'],
include: [resolve('src')], // src是項目開發的目錄
exclude: [path.resolve('../../node_modules')] // 不需要編譯node_modules下的js
},
// ... 其他loader
]
}
開啟babel-loader插件的cache
開啟babel-loader
的編譯緩存,以后編譯就只會編譯修改過的地方,重用的地方就不會重新編譯,大大節約構建速度。
此代碼在webpack.base.conf.js
中
module:{
rules: [
{
test: /\.js$/,
loader: ['babel-loader?cacheDirectory=true'] //修改這里即可
},
]
}
使用HappyPack加快編譯速度
雖然我們有webpack的熱更新,但每次初始化啟動項目時發現總是需要等待很長的時間才能完成編譯。
那么我們就可以利用HappyPack插件開啟多線程
,大大加快代碼構建速度。
主要核心技術有兩點:
- 利用Loader的并發可行性完成多線程
- 在每次編譯時候都會針對對應文件存入一個時間戳并且將編譯結果緩存 ,下次編譯時,就會先讀取這個時間戳。利用這個做key去對比,如果沒有變那么就直接讀取緩存。
例子代碼如下:
//先引入happypack與相關模塊
let HappyPack = require('happypack');
let os = require('os');
let happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module:{
rules: [{
test: /\.vue$/,
loader: 'vue-loader',
// options: vueLoaderConfig
options: {
loaders: {
js: 'happypack/loader?id=js' // 將loader換成happypack
}
}
},
{
test: /\.js$/,
// loader: 'babel-loader',
// loader: ['babel-loader?cacheDirectory=true'],
loader: ['happypack/loader?id=js'], // 將loader換成happypack
include: [resolve('src'), resolve('test')],
exclude: [path.resolve('../../node_modules')] // 不需要編譯node_modules下的js
},
...
}
plugins:[
new HappyPack({
id: 'vue',
threadPool: happyThreadPool,
cache: true,
verbose: true,
loaders: ['vue-loader'],
}),
new HappyPack({
id: 'js',
threadPool: happyThreadPool,
cache: true,
verbose: true,
loaders: ['babel-loader'],
}),
]
生產環境配置
何為生產環境配置:頁面部署到服務器時,為了追求性能加大用戶體驗,所以我們會對代碼進行各種各樣的優化,比如說混淆、壓縮,這些手段往往會徹底破壞代碼本身的可讀性,不利于我們進行debug等工作。
利用CommonsChuncksPlugin解決重復引用
我們在開發過程中為了方便代碼維護,可能會抽出一些公關的模塊,這里不止于vue
的共用組件,如果用了css預編譯器
,那更是會有@mixin
這種宏(scss是這樣,其他可能不是這個名詞但是也會有)。
那么在打包過程中將會把引入的模塊重復打包
,這樣大大增加了最后的文件體積,這樣是肯定不可取的。
而webpack有個插件叫CommonsChuncksPlugin
。
就是專門把重復打包的模塊給抽取出來單獨打包的插件。這個能夠顯著降低最后打包的體積,也能提升一些打包速度。
使用也很方便,就是在webpack.base.conf.js
中的plugins
添加相關的配置代碼,如下
plugins: [
new webpack.optimize.CommonsChunkPlugin({
async: 'shared-module',
minChunks: (module, count) => (
count >= 2 // 當一個模塊被重復引用2次或以上的時候單獨打包起來。
)
}),
//...
]