github傳送門 dev分支
webpack之二webpack部署優(yōu)化
webpack之三集成vuex和vue-Router
webpack之四集成ssr
-
創(chuàng)建項目根目錄
-
在vscode中打開, 在終端中輸入
// cd到項目目錄
cnpm init -y
生成package.json文件
-
安裝webpack和vue, vue-loader
cnpm i webpack vue-loader webpack@3 -D
cnpm i vue -S
// 安裝完成根據(jù)warning安裝需要的依賴
css-loader vue-template-compiler -D
-
創(chuàng)建src/app.vue
<template>
<div class="text">{{text}}</div>
</template>
<script>
export default {
data () {
return {
text: 'abc'
}
}
}
</script>
<style>
.text {
color: red;
}
</style>
-
創(chuàng)建/webpack.config.js
const path = require('path')
module.exports = {
entry: path.join(__dirname, 'src/index.js'),
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
}
}
-
創(chuàng)建/src/index.js 入口js文件
import Vue from 'vue'
import App from './app.vue'
const root = document.createElement('div')
document.body.appendChild(root)
new Vue({
render: h => h(App)
}).$mount(root)
-
安裝style-loader和css-loader, url-loader, file-loader, 處理css和圖片文件
cnpm i style-loader url-loader file-loader -D
url-loader依賴于file-loader
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(jpg|png|svg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name]-kay.[ext]'
}
}
]
}
]
}
-
安裝rimraf 在build之前刪除dist目錄
cnpm i rimraf -D
//在package.json.script中添加cli
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "npm run clean && webpack --config webpack.config.js",
"clean": "rimraf dist"
},
-
嵌入stylus: 安裝stylus和stylus-loader
cnpm i stylus stylus-loader -D
{
test: /\.styl$/,
use: [
'style-loader',
'css-loader',
'stylus-loader'
]
},
-
使用webpack-dev-server => 開發(fā)環(huán)境
安裝cross-env區(qū)分開發(fā)環(huán)境和生產(chǎn)環(huán)境
配置html模板html-webpack-plugin
配置webpack全局常量插件webpack.DefinePlugin()
變更webpack.config.js結(jié)構(gòu)
在package.json.script里添加dev命令
cnpm i webpac-dev-server@2 cross-env
-D
cnpm i html-webpack-plugin -D
//package.json.script
"dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js"
// 代碼
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const isDev = process.env.NODE_ENV === 'development'
const config = {
target: 'web',
resolve: {
alias: {
'@assets':path.join(__dirname, 'src/assets')
}
},
entry: path.join(__dirname, 'src/index.js'),
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.styl$/,
use: [
'style-loader',
'css-loader',
'stylus-loader'
]
},
{
test: /\.(jpg|png|svg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name]-kay.[ext]'
}
}
]
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: isDev ? '"development"' : '"production"'
}
}),
new HtmlWebpackPlugin()
]
}
if (isDev) {
config.devServer = {
port: 8001,
host: '0.0.0.0',
overlay: {
errors: true
}
}
}
module.exports = config
// 終端輸入 npm run dev啟動項目
-
webpack-dev-server配合vue熱更替功能實現(xiàn)
使用webpack.HotModuleReplacementPlugin和webpack.NoEmitOnErrorsPlugin
配置devServer.hot = true
css熱更替使用vue-style-loader代替style-loader
if (isDev) {
config.devServer = {
port: 8001,
host: '0.0.0.0',
overlay: {
errors: true
},
hot: true
}
config.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
)
}
-
配置source-map方便調(diào)試
config.devtool = '#cheap-module-eval-source-map'
- 配置jsx和postcss
安裝postcss-loader, autoprefixer babel-loader, babel-core,babel-preset-env, babel-plugin-transform-vue-jsx,babel-plugin-syntax-jsx, (babel-helper-vue-jsx-merge-props)
統(tǒng)一cnpm安裝下
創(chuàng)建/.babelrc, /postcss.config.js并分別配置
// .babelrc
{
"presets":[
"env"
],
"plugins": [
"transform-vue-jsx"
]
}
// postcss.config.js
const autoprefixer = require('autoprefixer')
module.exports = {
plugins: [
autoprefixer()
]
}
// 在devServer里添加babel-loader和postcss-loader
{
test: /\.js$/,
loader: 'babel-loader'
},
{
test: /\.jsx$/,
loader: 'babel-loader'
},
{
test: /\.styl$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
'stylus-loader'
]
},
-
測試jsx-vue模板
新建/src/views/test.jsx
export default {
data () {
return {
test: '這是用jsx語法寫的vue模板'
}
},
render () {
return (
<div>
{this.test}
</div>
)
}
}
-
css文件分離打包成單獨文件
使用extract-text-webpack-plugin
分離打包需要區(qū)分環(huán)境,刪除config目錄上的stylus-loader, 并在對應(yīng)環(huán)境下對應(yīng)loader配置
/webpack.config.js重寫后配置如下
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtartTextPlugin = require('extract-text-webpack-plugin')
const isDev = process.env.NODE_ENV === 'development'
const config = {
target: 'web',
resolve: {
alias: {
'@assets':path.join(__dirname, 'src/assets')
}
},
entry: path.join(__dirname, 'src/index.js'),
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.jsx$/,
loader: 'babel-loader'
},
{
test: /\.(jpg|png|svg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name]-kay.[ext]'
}
}
]
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: isDev ? '"development"' : '"production"'
}
}),
new HtmlWebpackPlugin()
]
}
if (isDev) {
config.module.rules.push({
test: /\.styl$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
'stylus-loader'
]
})
config.devtool = '#cheap-module-eval-source-map'
config.devServer = {
port: 8001,
host: '0.0.0.0',
overlay: {
errors: true
},
hot: true
}
config.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
)
} else {
config.module.rules.push({
test: /\.styl$/,
use: ExtartTextPlugin.extract({
fallback: "style-loader",
use: [
'css-loader',
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
'stylus-loader'
]
})
})
config.plugins.push(
new ExtartTextPlugin({
filename: 'style.[contenthash:8].css'
})
)
}
module.exports = config
-
單獨打包類庫文件, 實現(xiàn)緩存優(yōu)化性能
修改線上環(huán)境entry選項使用options模式為js文件添加name屬性app和vendor
-> 是output中[name]的由來,將開發(fā)環(huán)境的output.filename修改為'bundle.[hash:8].js',
正式環(huán)境修改為: '[name].[chunkhash:8].js'(不可以使用hash, webpack-dev-server會報錯)
hash和chunkhash的區(qū)別:使用hash會讓app和vendor的hash碼一樣是整個應(yīng)用的hash(如果某個js文件改變了, 說有js文件的hash也會跟著改變, 正式環(huán)境就 沒有意義了), 而chunkhash是每一個模塊或者說節(jié)點單獨生成hash,
使用
webpack.optimize.CommonsChunkPlugin({name: 'vendor'})
單獨打包webpack相關(guān)的js代碼:(好處是在有新的模塊加入時候, webpack會給模塊加id, 如果在中間插入會改變之前代碼的id, 這樣會導(dǎo)致hash變化破壞緩存, 需要單獨打包)vendor必須在runtime之前
使用webpack.optimizeCommonsChunkPlugin({name: 'runtime'})
/webpack.config.js修改為:
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtartTextPlugin = require('extract-text-webpack-plugin')
const isDev = process.env.NODE_ENV === 'development'
const config = {
target: 'web',
resolve: {
alias: {
'@assets':path.join(__dirname, 'src/assets')
}
},
entry: path.join(__dirname, 'src/index.js'),
output: {
filename: 'bundle.[hash:8].js',
path: path.join(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.jsx$/,
loader: 'babel-loader'
},
{
test: /\.(jpg|png|svg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name]-kay.[ext]'
}
}
]
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: isDev ? '"development"' : '"production"'
}
}),
new HtmlWebpackPlugin()
]
}
if (isDev) {
config.module.rules.push({
test: /\.styl$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
'stylus-loader'
]
})
config.devtool = '#cheap-module-eval-source-map'
config.devServer = {
port: 8001,
host: '0.0.0.0',
overlay: {
errors: true
},
hot: true
}
config.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
)
} else {
config.entry = {
app: path.join(__dirname, 'src/index.js'),
vendor: ['vue']
}
config.output.filename = '[name].[chunkhash:8].js'
config.module.rules.push({
test: /\.styl$/,
use: ExtartTextPlugin.extract({
fallback: "style-loader",
use: [
'css-loader',
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
'stylus-loader'
]
})
})
config.plugins.push(
new ExtartTextPlugin({
filename: 'style.[contenthash:8].css'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
})
)
}
module.exports = config
-
生成環(huán)境壓縮js
使用webpack.optimize.UglifyJsPlugin()并配置options.compress.warnings = false, options.output.comments: false
if(isDev) {
...
} else {
...
config.plugins.push(
new ExtartTextPlugin({
filename: 'style.[contenthash:8].css'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
output: {
comments: false
}
})
)
}
-
生成環(huán)境壓縮css
- 安裝
optimize-css-assets-webpack-plugin@3
- 配置生成環(huán)境插件
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.css$/,
cssProcessor: require('cssnano'),
cssProcessorOptions: { discardComments: { removeAll: true } },
canPrint: true
})
這一份簡單webpack配置, 可以讓vue跑起來, 但是遠遠不夠, 接下來還會更新, 能夠部署上線的配置和ssr服務(wù)器端渲染的配置
wepack從0開始配置vue環(huán)境之二: 線上部署配置優(yōu)化
github地址可以下載源碼來研究