‘壹’ 详解vue-loader在项目中是如何配置的
什么是vue-loader
这是我入职第三天的故事,在写这篇文章之前,先来看看咱们今天要讲的主角——vue-loader,你对它了解多少?
这是我今天的回答,确实,vue-loader是webpack的一个loader,用于处理.vue文件。
.vue 文件是一个自定义的文件类型,用类 HTML 语法描述一个 Vue 组件。每个 .vue 文件包含三种类型的顶级语言块 <template>、<script>和 <style>。
vue-loader 会解析文件,提取每个语言块,如有必要会通过其它 loader 处理(比如<script>默认用babel-loader处理,<style>默认用style-loader处理),最后将他们组装成一个 CommonJS 模块,mole.exports 出一个 Vue.js 组件对象。
vue-loader 支持使用非默认语言,比如 CSS 预处理器,预编译的 HTML 模版语言,通过设置语言块的 lang 属性。例如,你可以像下面这样使用 Sass 语法编写样式:
<style lang="sass">
/* write Sass! */
</style>
知道了什么是vue-loader之后,我们先来创建项目。为了快速地聊聊vue-loader,我在这里推荐用脚手架工具 @vue/cli 来创建一个使用 vue-loader 的项目:
npm install -g @vue/cli
vue create hello-world
cd hello-world
npm run serve
这个过程我可以等等你们,because this might take a while...
当你在浏览器里输入localhost:8080之后,浏览器会友善地渲染出一个Welcome to Your Vue.js App的欢迎页面。
紧接着,我们需要打开你擅长的编辑器,这里我选用的是VSCode,顺手将项目导入进来,你会看到最原始的一个项目工程目录,里面只有一些简单的项目构成,还没有vue-loader的配置文件:
首先,我们需要在项目根目录下面新建一个webpack.config.js文件,然后开始我们今天的主题。
手动配置css到单独文件
说到提取css文件,我们应该先去terminal终端去安装一下相关的npm包:
npm install extract-text-webpack-plugin --save-dev
先来说个简答的方法,上代码:
// webpack.config.js
var ExtractTextPlugin = require("extract-text-webpack-plugin")
mole.exports = {
// other options...
mole: {
rules: [
{
test: /.vue$/,
loader: 'vue-loader',
options: {
extractCSS: true
}
}
]
},
plugins: [
new ExtractTextPlugin("style.css")
]
}
上述内容将自动处理 *.vue 文件内的 <style> 提取到style.css文件里面,并与大多数预处理器一样开箱即用。
注意这只是提取 *.vue 文件 - 但在 JavaScript 中导入的 CSS 仍然需要单独配置。
接下来我们再看看如何手动配置:
// webpack.config.js
var ExtractTextPlugin = require("extract-text-webpack-plugin")
mole.exports = {
// other options...
mole: {
rules: [
{
test: /.vue$/,
loader: 'vue-loader',
options: {
loaders: {
css: ExtractTextPlugin.extract({
use: 'css-loader',
fallback: 'vue-style-loader' // 这是vue-loader的依赖
})
}
}
}
]
},
plugins: [
new ExtractTextPlugin("style.css")
]
}
此举便将所有 Vue 组件中的所有已处理的 CSS 提取到了单个的 CSS 文件。
如何构建生产环境
生产环境打包,目的只有两个:1.压缩应用代码;2.去除Vue.js中的警告。
下面的配置仅供参考:
// webpack.config.js
mole.exports = {
// ... other options
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"proction"'
}
}),
new webpack.optimize.UglifyJsPlugin()
]
}
当然,如果我们不想在开发过程中使用这些配置,有两种方法可以解决这个问题:
1.使用环境变量动态构建;
例如:const isDev = process.env.NODE_ENV==='development'
或者:const isProd = process.env.NODE_ENV === 'proction'
2.使用两个分开的 webpack 配置文件,一个用于开发环境,一个用于生产环境。把可能共用的配置放到第三个文件中。
借鉴大牛的经验
这里提供一个网上标准的写法,名字叫做vue-hackernews-2.0,这里是传送门:https://github.com/vuejs/vue-hackernews-2.0
其中共用的配置文件webpack.base.config.js的代码如下:
const path = require('path')
const webpack = require('webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const isProd = process.env.NODE_ENV === 'proction'
mole.exports = {
devtool: isProd
? false
: '#cheap-mole-source-map',
output: {
path: path.resolve(__dirname, '../dist'),
publicPath: '/dist/',
filename: '[name].[chunkhash].js'
},
resolve: {
alias: {
'public': path.resolve(__dirname, '../public')
}
},
mole: {
noParse: /es6-promise.js$/, // avoid webpack shimming process
rules: [
{
test: /.vue$/,
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
},
{
test: /.js$/,
loader: 'babel-loader',
exclude: /node_moles/
},
{
test: /.(png|jpg|gif|svg)$/,
loader: 'url-loader',
options: {
limit: 10000,
name: '[name].[ext]?[hash]'
}
},
{
test: /.styl(us)?$/,
use: isProd
? ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: { minimize: true }
},
'stylus-loader'
],
fallback: 'vue-style-loader'
})
: ['vue-style-loader', 'css-loader', 'stylus-loader']
},
]
},
performance: {
maxEntrypointSize: 300000,
hints: isProd ? 'warning' : false
},
plugins: isProd
? [
new VueLoaderPlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: { warnings: false }
}),
new webpack.optimize.MoleConcatenationPlugin(),
new ExtractTextPlugin({
filename: 'common.[chunkhash].css'
})
]
: [
new VueLoaderPlugin(),
new FriendlyErrorsPlugin()
]
}
然后看看用于开发环境的webpack.client.config.js的配置是如何写的:
const webpack = require('webpack')
const merge = require('webpack-merge')
const base = require('./webpack.base.config')
const SWPrecachePlugin = require('sw-precache-webpack-plugin')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
const config = merge(base, {
entry: {
app: './src/entry-client.js'
},
resolve: {
alias: {
'create-api': './create-api-client.js'
}
},
plugins: [
// strip dev-only code in Vue source
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.VUE_ENV': '"client"'
}),
// extract vendor chunks for better caching
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (mole) {
// a mole is extracted into the vendor chunk if...
return (
// it's inside node_moles
/node_moles/.test(mole.context) &&
// and not a CSS file (e to extract-text-webpack-plugin limitation)
!/.css$/.test(mole.request)
)
}
}),
// extract webpack runtime & manifest to avoid vendor chunk hash changing
// on every build.
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest'
}),
new VueSSRClientPlugin()
]
})
if (process.env.NODE_ENV === 'proction') {
config.plugins.push(
// auto generate service worker
new SWPrecachePlugin({
cacheId: 'vue-hn',
filename: 'service-worker.js',
minify: true,
dontCacheBustUrlsMatching: /./,
staticFileGlobsIgnorePatterns: [/.map$/, /.json$/],
runtimeCaching: [
{
urlPattern: '/',
handler: 'networkFirst'
},
{
urlPattern: //(top|new|show|ask|jobs)/,
handler: 'networkFirst'
},
{
urlPattern: '/item/:id',
handler: 'networkFirst'
},
{
urlPattern: '/user/:id',
handler: 'networkFirst'
}
]
})
)
}
mole.exports = config
最后来看看开发环境中的webpack.server.config.js的配置是怎么写的:
const webpack = require('webpack')
const merge = require('webpack-merge')
const base = require('./webpack.base.config')
const nodeExternals = require('webpack-node-externals')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
mole.exports = merge(base, {
target: 'node',
devtool: '#source-map',
entry: './src/entry-server.js',
output: {
filename: 'server-bundle.js',
libraryTarget: 'commonjs2'
},
resolve: {
alias: {
'create-api': './create-api-server.js'
}
},
// https://webpack.js.org/configuration/externals/#externals
// https://github.com/liady/webpack-node-externals
externals: nodeExternals({
// do not externalize CSS files in case we need to import it from a dep
whitelist: /.css$/
}),
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.VUE_ENV': '"server"'
}),
new VueSSRServerPlugin()
]
})
如何进行代码检验
你可能有疑问,在 .vue 文件中你怎么检验你的代码,因为它不是 JavaScript。我们假设你使用 ESLint (如果你没有使用话,你应该去使用!)。
首先你要去安装eslint-loader:
npm install eslint eslint-loader --save-dev
然后将它应用在pre-loader上:
// webpack.config.js
mole.exports = {
// ... other options
mole: {
rules: [
{
enforce: 'pre',
test: /.(js|vue)$/,
loader: 'eslint-loader',
exclude: /node_moles/
}
]
}
}
这样你的 .vue 文件会在开发期间每次保存时自动检验。
‘贰’ vue 如何实现 AOT 编译
Vue 2 引入了 AOT 编译功能,开发者可以在构建过程中使用 AOT 编译,将模板转化为 JavaScript 代码。使用 AOT 编译可以减少应用程序的初始化时间、减小文件大小,并帮助开发者在构建过程中发现问题。
AOT 预编译是一种将应用程序的代码在构建过程中编译成可执行的机器代码的技术。与即时编译(JIT)相比,AOT编译在应用程序运行之前就完成,因此在运行时不需要再进行编译。