一直都覺得SSR是一個挺麻煩的事情,牽扯的知識範圍還挺大的,尤其是用vue-cli 工具,遮蔽了許多配置的細節。
但在使用SSR,不用Nuxt.js 的時候來做SSR,還是挺難上手的,索性好好捋一遍這方面的相關知識,總結成了一個系列的文章。作為 SSR 文件的一個補充,希望對大家有所幫助。
目標
這篇文章的主要目的解讀一個簡單的 vue-webpack 如何搭建和每一個外掛的作用。完成一個 client-only的 vue-webpack 開發環境,具備以下的功能:
- 處理 vue 單檔案元件
- 編譯 ES6
- 編譯 Less 或者 Sass
- 載入圖片
- 開發伺服器
- 熱載入
- 定義環境變數
- 能區分生產環境進行壓縮
對這相關配置已經非常瞭解了的同學可以直接關閉了。
新增基本功能
從這一節開始會貼出一步一步實現的程式碼,儘量還原整個配置的細節。
建立專案檔案
$ mkdir simple-webpack && cd simple-webpack
$ npm init複製程式碼
然後按照下面的目錄結構新建檔案。
.
├── index.html
├── package.json
├── src
│ ├── App.vue
│ ├── app.js
│ └── assets
└── webpack.config.js複製程式碼
安裝對應依賴
需要安裝 webpack 需要的依賴有:
- webpack
- vue-loader
- babel-core
- css-loader
- babel-loader
- file-loader
- sass-loader
- node-sass
依次安裝時,把安裝依賴儲存到package.json,以便下次在不同的環境下使用時,能快速的安裝依賴。
新增Vue程式碼
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>simple - webpack</title>
</head>
<body>
<div id="app"></div>
<script src="/dist/build.js"></script>
</body>
</html>複製程式碼
App.vue
<template>
<div class="demo">
<h1>Simple-webpack demo</h1>
<p>這是一個簡單的 Vue demo</p>
</div>
</template>
<script>
</script>
<style>
</style>複製程式碼
app.js
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: '#app',
render: h => h(App)
})複製程式碼
新增 webpack.config 配置
先寫好我們的入口檔案和輸出檔案的地址和打包後的檔名。
const webpack = require('webpack')
const path = require('path')
module.exports = {
entry: './src/app.js',
output: {
path: path.resolve(__dirname, './dist/'),
filename: 'build.js'
}
}複製程式碼
新增 vue-loader
module.exports = {
entry: './src/app.js',
output: {
path: path.resolve(__dirname, './dist/'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: ''
}
]
}
}複製程式碼
此時執行 webpack
打包就可以打包成一個可用的程式了,大家可以自行打包後將檔案放在靜態伺服器中執行。
完成這個基本的打包,用到的webpack loader 和包有:
- vue-loader GitHub - vuejs/vue-loader
- babel-core babel-core
- css-loader css-loader
這個包 npm 官網上都有詳細的介紹,這裡就不贅述了,大家可以自行去看各個loader 在上面的打包過程中完成什麼樣工作。
為了讓我們編寫的程式碼能在低版本的瀏覽器中使用,我們新增 babel-loader,在打包的時候將檔案中的 ES6 語法轉成 .bablerc 中配置的版本。
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: ''
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [path.resolve(__dirname, './src')]
}
]
}複製程式碼
在目錄下建立 .babelrc,配置內容如下:
{
"presets": [
["env", { "modules": false }]
]
}複製程式碼
然後需要安裝一個 babel 外掛,babel-preset-env, 關於這個外掛的作用具體參見 babel-preset-env
: a preset that configures Babel for you
給 App.vue 中新增圖片:
<template>
<div class="demo">
<h1>Simple-webpack demo</h1>
<p>這是一個簡單的 Vue demo</p>
<img src="./assets/logo.png" alt="">
</div>
</template>複製程式碼
因為新增了圖片,再執行 webpack 打包的時候,webpack 會報錯,因為沒有對應的 loader 去載入這些二進位制檔案。
新增 file-loader:
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: ''
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [path.resolve(__dirname, './src')]
},
{
test: /\.(png|jpg|svg|git)$/,
loader: 'file-loader',
include: [path.resolve(__dirname, './src/assets')]
}
]
},複製程式碼
目前我們在單檔案元件中,可以使用 css,但是還不能使用 sass,我再新增一個對應的 sass-loader 來處理 sass 檔案,因為 css/sass 是 vue-loader 在做程式碼分割的時候分割出來的文字段,我們只需要在 vue-loader 的 options 中新增對應的配置。
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
'scss': 'vue-style-loader!css-loader!sass-loader',
'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax'
}
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [path.resolve(__dirname, './src')]
},
{
test: /\.(png|jpg|svg|git)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},複製程式碼
到目前為止,一個具備打包編譯 Vue 專案的 webpack 環境配置已經寫好了。接下來我們新增兩個比較重要的輔助工具 devServer 和 hot replace。
新增Dev Server 和熱更新
新增 Dev Server 的方式的方式有兩種:
- webpack-dev-server
- webpack-dev-middleware
兩種的使用方式和配置可以看這篇官網介紹 開發。這裡我們選用一種比較簡單的方式,直接使用 webpack-dev-server。
先安裝 npm install -S webpack-dev-server
,再修改 package.json 新增 npm script,程式碼如下:
"scripts": {
"test": "",
"dev": "webpack-dev-server --open"
}複製程式碼
open 引數代表服務啟動後會自動在瀏覽器中開啟頁面。然後再開啟 webpack.config.js 檔案,新增 devServer 的相關配置。除了以上的配置還能修改 host 和 port,這裡我們使用預設就行。新增 hot replace 也是非常簡單的,webpack 自帶了這一個 plugin,具體使用方法可以看 模組熱替換,新增的配置程式碼如下:
module: {
//...
},
devServer: {
historyApiFallback: true,
hot: true,
noInfo: false,
overlay: true
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]複製程式碼
新增環境變數
process
是 node 中的一個模組,我們可以用它的 env 變數來區分 shell 的環境變數。這樣我們就可以通過 npm run dev
和 npm run build
來區分我們是開發還是生產構建。
先修改兩個 npm script 來區分 shell 環境變數,這裡我們藉助 cross-env npm 模組實現自定義的環境變數。
"scripts": {
"test": "",
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack"
}複製程式碼
// webpack.config.js
module.exports = {
//...
}
console.log(process.env.NODE_ENV)
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}複製程式碼
在 production 環境中新增了兩個外掛,優化打包的程式碼和壓縮 JS 程式碼。第一個外掛 DefinePlugin 是為前端程式碼提供與 webpack 一致的環境變數,便於我們在業務程式碼中區分不同的環境,Vue 框架中也要根據這個環境變數來切分開發環境和生產環境。
我們再稍微整理一下配置,一個簡單 client-only 的 webpack 的配置就寫好了。
const webpack = require('webpack')
const path = require('path')
module.exports = {
entry: './src/app.js',
output: {
path: path.resolve(__dirname, './dist/'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
'scss': 'vue-style-loader!css-loader!sass-loader',
'sass': 'vue-style-loader!css-loader!sass-loader'
}
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [path.resolve(__dirname, './src')]
},
{
test: /\.(png|jpg|svg|git)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
}
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
} else {
module.exports.devtool = '#eval-source-map'
module.exports.devServer = {
historyApiFallback: true,
hot: true,
noInfo: false,
overlay: true
}
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.HotModuleReplacementPlugin()
])
}複製程式碼
本文只是梳理打造一個簡單配置的過程,為後面的 SSR 配置作為基礎。webpack 的配置項非常多,而 vue-cli 中提供的 webpack 配置遠沒有那麼簡單。更進階的方式可以閱讀參考中文章。