由于vue-cli进入3.0以来,其打包的配置被隐藏到了@vue/cli-service
包里,webpack配置的修改只能通过vue.config.js
进行。
"@vue/cli-plugin-babel": "^4.1.2",
"@vue/cli-plugin-eslint": "^4.1.2",
"@vue/cli-plugin-typescript": "^4.1.2",
"@vue/cli-plugin-unit-jest": "^4.1.2",
"@vue/cli-service": "^4.1.2", //vue-cli脚手架
这种模式虽然极大的简化了开发人员的项目配置,但对于想提升自我的前端来说,无疑是不太友好的。
虽然webpack配置较为繁杂,但只要掌握了规律,写起配置就没难了,而且webpack经过这几年的发展也有了中文文档
总结起来有四个方面的配置:
下面这段(
webpack.base.js
)配置就是webpack打包入口/出口
配置。
...... //省略了各模块的引入
module.exports = {
...
context: path.resolve(__dirname, '../'),
entry: './src/main.ts', //打包的入口文件,这里使用了.ts
output: {
path: path.resolve(__dirname, '../dist'), //打包后的输出文件
filename: 'js/[name].js',//打包后的文件命名规则,这里保持文件名
publicPath: '/' //打包后文件的引用路径,默认为当前路径'/'
},
target: 'web' // 输出目标浏览器
.......
常用的自动后缀名和路径别名解析,如下:
resolve: {
//自动识别的文件后缀,比如写 from '@/store',会被依次解析为 '绝对路径/store.mjs/.js等'直到找到文件
extensions: ['.mjs', '.js', '.ts', '.tsx', '.jsx', '.vue', '.json', '.wasm', '.scss'],
alias: {
'@': resolve('src'), //src路径别名
vue$: 'vue/dist/vue.esm.js'
}
},
往下是webpack最为核心的loader配置,虽然webpack的loader各种各样,但是使用起来十分简单,如这段
vue + babel + ts + scss + postcss
的loader配置:
module: {
rules: [
{
test: /\.vue$/, //正则匹配.vue后缀文件,使用vue-loader这个loader进行解析
use: [{loader: 'vue-loader'} ]
},
{
test: /\.js$/, //正则匹配.js后缀文件,使用babel-loader进行解析
exclude: /node_modules/,
use: [{loader: 'babel-loader'} ]
},
{
test: /\.tsx?$/, //正则匹配.ts|tsx后缀文件,使用ts-loader进行解析,这里就是使用TypeScript的关键配置
use: [
{loader: 'babel-loader'},
{loader: 'ts-loader',options: {transpileOnly: true,appendTsSuffixTo: ['\\.vue$'],happyPackMode: true}}]
},
{
test: /\.(css|scss)$/, //解析scss样式,同时使用了postcss对css进行浏览器兼容等处理
use: ['vue-style-loader','css-loader','postcss-loader','sass-loader']
}
]
},
这样就非常简单的配置好了vue、ts、scss的代码转换;
但需要处理一个新文件类型时,总结起来就是三步:
test
;webapck的插件非常丰富,正是有了这些插件,才使得打包项目更加简单起来:
......
plugins: [ //webpack的插件配置,大部分插件都是引入即可用,无需配置
new VueLoaderPlugin(), //vue-loader的伴生插件
new CleanWebpackPlugin(), //清除./dist文件夹
new ProgressPlugin(), // 显示打包进度
new HtmlWebpackPlugin({ //自动注入打包后的.js文件
filename: 'index.html',
template: resolve('/public/index.html'),
chunksSortMode: 'none' // 不对文件引入自动排序
}),
new CopyWebpackPlugin([ //直接拷贝静态文件至输出文件夹
{
from: './public',
to: './public', // /dist/public
toType: 'dir',
ignore: ['.DS_Store', '*.html']
}])
]
......
以上这些,便是webpack打包的内容了,有了这些配置,我们的vue项目已经可以完成打包了,是不是并没有想象中的复杂?
完整代码:
const path = require('path')
const webpack = require('webpack')
const ProgressPlugin = require('progress-bar-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
const isProduction = process.env.NODE_ENV === 'production'
const inlineLimit = 4096
module.exports = {
mode: isProduction ? 'production' : 'development',
target: 'web',
context: path.resolve(__dirname, '../'), //用于从配置中解析入口起点(entry point)和 loader
entry: './src/main.ts',
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'js/[name].js',
publicPath: '/' //所有资源请求的路径,可以为url,或者前缀,默认为''
},
resolve: {
extensions: ['.mjs', '.js', '.ts', '.tsx', '.jsx', '.vue', '.json', '.wasm', '.scss'],
alias: {
'@': resolve('src'),
vue$: 'vue/dist/vue.esm.js'
},
modules: ['node_modules', resolve('node_modules')]
},
module: {
noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/,
rules: [
{
test: /\.vue$/,
use: [
{
loader: 'vue-loader'
}
]
},
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader'
}
]
},
{
test: /\.tsx?$/,
use: [
{
loader: 'babel-loader'
},
{
loader: 'ts-loader',
options: {
transpileOnly: true,
appendTsSuffixTo: ['\\.vue$'],
happyPackMode: true
}
}
]
},
{
test: /\.(css|scss)$/,
use: [
'vue-style-loader',
'css-loader',
'postcss-loader',
{
loader: 'sass-loader',
options: {
sourceMap: true,
//sass-loader更新到8.0.0,data=>prependData
prependData: '@import "@/assets/scss/variable.scss";'
}
}
]
},
{
test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: inlineLimit,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
}
},
{
test: /\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i,
loader: 'url-loader',
options: {
limit: inlineLimit,
fallback: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]'
}
}
}
}
]
},
plugins: [
new VueLoaderPlugin(),
new ProgressPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: resolve('/public/index.html'),
chunksSortMode: 'none'
}),
new CopyWebpackPlugin([
{
from: './public',
to: './public',
toType: 'dir',
ignore: ['.DS_Store', '*.html']
}
]),
// 异步检查ts代码
new ForkTsCheckerWebpackPlugin({
vue: true,
tslint: true,
formatter: 'codeframe',
checkSyntacticErrors: true
})
]
}
完整开发依赖:
yarn add -D webpack webpack-merge vue-loader @babel/core babel-loader typescript ts-loader vue-style-loader css-loader postcss-loader autoprefixer sass node-sass sass-loader url-loader file-loader progress-bar-webpack-plugin copy-webpack-plugin html-webpack-plugin fork-ts-checker-webpack-plugin
最后,如果要在dev下查看效果,只需要:
build
下创建webpack.dev.js
;const { HotModuleReplacementPlugin, DefinePlugin } = require('webpack')
const baseConfig = require('./webpack.base')
const webpackMerge = require('webpack-merge')
module.exports = webpackMerge(baseConfig, {
devtool: 'cheap-module-eval-source-map', // 使用sourceMap
devServer: {
contentBase: false, // 由于使用了CopyWebpackPlugin.
historyApiFallback: true, // 路由为history模式时需开始
hot: true, // 开启代码热更新
inline: true,
host: 'localhost', // 如果需要局域网内访问,可设置为0.0.0.0
port: 8080,
open: true, // 编译后自动打开浏览器
},
plugins: [
new DefinePlugin({
'process.env': {
NODE_ENV: '"development"' // 定义为开发模式
},
BASE_URL: '"./"' // 定义站点根路径,当部署路径不为/时,可在此设置,如/app1/
}),
new HotModuleReplacementPlugin(), // 代码热更新插件
compilationSuccessInfo: {
messages: [`项目运行地址: http://${devConfig.host}:${devConfig.port}`]
}
})
]
})
package.json
的scripts
中加上: "dev": "cross-env process.env.NODE_ENV=development webpack-dev-server --config build/webpack.dev.js"
yarn add -D cross-env webpack-dev-server
npm run dev
或者yarn dev
就可以看到效果了。