一、vue-cli 是个啥
vue init webpack xxx
vue create xxx
这哥俩相信大家都不陌生吧,其实就是实现了开箱即用的 vue 开发环境而已,也就是我们所说的脚手架。说来,一直觉得脚手架是个神奇的东西,简单的一行指令就可以配置好整个项目的开发环境,相信很多人都是这样做的吧,当然我也不例外,谁让它方便呢!
关于脚手架,其实真的没有那么神奇,可以来这儿看看眼熟不?是不是突然觉得脚手架也没那么神秘了?可以把它当做是一个下载器吧,帮我们做了的就是把我们想要模板下载好而已,真正厉害的说到底还是 webpack。
二、vue 环境搭建实践
因为 vue-cli 的特性,就证明了它并不能满足有针对性的需要,它只是为了迎合大众的需求,所以我在想,为什么不能自己搭一个 vue 的开发环境呢?
盘它!
2.1 初始化
npm init -y
or
yarn init -y
这不用多说吧,初始化,生成 package.json,这里我就用 yarn 了,之后我安装了 vue 、webpack 和 webpack-cli,webpack-cli 是一个简易的客户端,用以 webpack 协议连接相应服务,从 webpack4 开始它们分离了,装它。
yarn add -D vue
yarn add -D webpack webpack-cli
之后系统会自动创建 package.json 文件,并且里面是这个样子的:
2.2 搞事情
初始化好项目之后,我们来随便创建点什么,如下图:
然后 com.js 里面的内容如下:
import Vue from "vue/dist/vue.esm";
const vm = new Vue({
el: "#app",
data: {},
template: `<div><com /></div>`,
components: {
com: {
data() {
return { message: '我是 Nyya' }
},
template: `<div><h1>{{message}}</h1></div>`
}
}
});
现在需要我们简单的配置一下我们的主角 webpack,在 webpack.config.js 中写如下代码 :
const path = require('path');
module.exports = {
// development, production 或 none,默认值为 production,来设置 webpack 内置环境
mode: 'none',
// 指示 webpack 应该使用哪个模块来作为开始
entry: './src/components/com.js',
// 在哪里输出它所创建的 bundle
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
}
好了,是不是非常简单呢,让我们来看下结果吧!运行下面的指令它会自动生成 dist 目录和 bundle.js 文件。
npx webpack
这时我们在 index.html 中引入我们打包后的 js 并且用浏览器打开。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack</title>
</head>
<body>
<div id="app"></div>
<script src="./dist/bundle.js"></script>
</body>
</html>
怎么样,是不是很简单,然后我们尝试更改 com.js 中 message 的内容,然后刷新网页,发现它并不会更新网页内容,这其实需要我们重新打包发布,这样的话是不是很麻烦,所以我们接下来搞它!
2.3 热更新
yarn add -D webpack-dev-server
这里我们需要用到 webpack 的一个小弟,webpack-dev-server。
它为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)
webpack-dev-server 打包后的 js 文件其实是在内存中维护的,所以不能直接使用这个 web 服务。那么接下来,我们要怎么配置 webpack 呢?
接着盘!
这个时候我们把 webpack.config.js 中的内容改成如下:
module.exports = env => {
console.log(env);
return {
entry: './src/components/com.js',
}
}
接着我们执行
npx webpack --env.production
or
npx webpack --env.development
我们会发现它打印出来的是这样的:
这时我们在项目根目录下创建一个 config 文件夹,并且里面创建 webpack.production.js 和 webpack.development.js 文件,里面的内容如下:
> webpack.production.js
console.log('pro');
> webpack.development.js
console.log('dev');
将 webpack.config.js 文件的内容改成:
module.exports = env => {
env = env || {};
return {
entry: './src/components/com.js',
...(env.production ? require('./config/webpack.production') : require('./config/webpack.development'))
}
}
分别执行一下两个指令:
npx webpack --env.production
or
npx webpack --env.development
到现在这一步是不是觉得还差点什么?没错!我们来一起处理下 package.json 吧:
{
"name": "webpack-vue-cli-my",
"version": "1.0.0",
"main": "index.js",
"author": "Nyya",
"license": "MIT",
"private": true,
"scripts": {
"serve": "webpack-dev-server --env.development --open",
"build": "webpack --env.production"
},
"devDependencies": {
"vue": "^2.6.12",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
}
}
--open 参数可以帮我们跑起来服务后直接在浏览器中打开
这里我只是加了 scripts,看起来是不是眼熟了?用起来是不是亲切了?是不是很激动?是不是很兴奋?
yarn serve or yarn build
npm run serve or npm run build
在这里我要说明一下,我们在开发中其实就是为了使用内存中的数据,而打包出来的才是我们服务器中要跑的代码,也是我们开发完毕后的代码。
现在我们将 config/webpack.development.js 文件改成如下代码:
module.exports = {
mode: 'development',
output: {
filename: 'bundle.js'
}
}
将 config/webpack.production.js 文件改成如下代码:
const path = require('path');
module.exports = {
mode: 'production',
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'bundle.min.js'
}
}
这里的 filename 改为 bundle.min.js 是为了在打包时压缩 js 代码
好,让我们跑起来我们的服务吧,当然因为我们的 serve 模式是在内存中的,所以要把 index.html 文件中
<script src="./dist/bundle.js"></script>
改成
<script src="/bundle.js"></script>
yarn serve 一下,在我们更改 com.js 中的 message 时,网页是不是已经可以时时渲染最新的内容啦!
2.4 html-webpack-plugin
上一步我们已经实现了热更新,但是聪明的你应该注意到了一个问题,我们在开发和生产两套不同的环境下,需要手动修改 index.html 中对我们打包后的 js 文件的引用,这真是太烦了有木有!
所以在这里我再介绍一个跟 webpack 和 html 都是兄弟的角色,它就是 html-webpack-plugin
html-webpack-plugin: 是 webpack 插件,可以将 html 打包
yarn add -D html-webpack-plugin
现在我们将 config/webpack.development.js 文件改成如下代码:
const htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
output: {
filename: 'bundle.js'
},
plugins: [
new htmlWebpackPlugin({
template: 'public/index.html',
filename: 'index.html'
})
]
}
将 config/webpack.production.js 文件改成如下代码:
const path = require('path');
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: 'production',
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'bundle.min.js'
},
plugins: [
new htmlWebpackPlugin({
template: 'public/index.html',
filename: 'index.html'
})
]
}
这里通常我们将 index.html 文件放入根目录下的 public 文件夹中,并且把 js 引入的代码部分注释掉
<--! <script src="/bundle.js"></script> -->
目前,我的项目目录就变成了酱紫滴:
让我们跑一下生产和开发环境试试吧!我这里是没有问题的,开发环境可以热更新,生产环境可以自动 引入 bundle.min.js
三、 webpack => loader、plugin
到了这一步,突然发现我们写的是 js 啊,打包的也是 js,但是我们是 vue 开发,当然要写 vue 了,在这里就要引入 webpack loader 了,它是干什么用的呢?
在 webpack 中,我们想让它识别除 js 和 json 以外的文件,就需要安装 loader 了。
在上一节我们见到的 plugin,其实就是插件。
3.1 初始化
好,废话不多说,项目升级,既然咱要开始写 vue,那么就要让 webpack 认识 vue,如下:
yarn add -D vue-html-loader // 解析 vue2.0 组件里的 template
yarn add -D vue-loader // 解析 vue 文件
yarn add -D vue-template-compiler // 编译器,把 vue 分别编译成 html js css
装好之后呢,我们再升级一下我们的项目结构如下:
是不是很眼熟了呢?我们在向 cli 搭建出来的环境靠拢啊,但是可以根据我们不同的需求,做出不同的调整。
这时我们来写一下新创建的这几个文件把。
- 首先是 main.js,内容如下:
import Vue from "vue/dist/vue.esm";
import App from "./App.vue";
new Vue({
el: "#app",
components: {
App,
},
template: `
<div>
<app></app>
</div>
`,
});
- 然后是 App.vue,如下:
<template>
<div>
<com><com />
</div>
</template>
<script>
import Com from "./components/com.vue";
export default {
name: "App",
components: {
Com,
},
};
</script>
- 其次是我们的组件 com.vue,如下:
<template>
<div>
<h1>{{ message }}</h1>
</div>
</template>
<script>
export default {
name: "Com",
data() {
return {
message: "我是 Nyya",
};
},
};
</script>
当然我们的项目入口就要改成 main.js 了,在 webpack.config.js 中改成如下配置:
module.exports = env => {
env = env || {};
return {
entry: './src/main',
...env.production ? require('./config/webpack.production') : require('./config/webpack.development')
}
}
这时我们启动项目是会报错的,因为 webpack 此时并不认识 vue 文件
3.2 loader
loader 描述了 webpack 如何处理非 JavaScript(non-JavaScript) 模块,说白了就是为了让 webpack 认识除了 js 以外的文件。
上一步我们已经装好了需要的 loader 并且创建了 vue 和 main.js 文件,现在我们需要升级我们的 webpack 配置了
先从 webpack.config.js 开始:
module.exports = env => {
env = env || {};
return {
entry: "./src/main",
module: {
rules: [
{ test: /\.vue$/, use: 'vue-loader' }
]
},
...(env.production ? require('./config/webpack.production') : require('./config/webpack.development'))
}
}
其次是 config/webpack.development.js:
const htmlWebpackPlugin = require('html-webpack-plugin');
const vueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
mode: "development",
output: {
filename: "bundle.js"
},
plugins: [
new htmlWebpackPlugin({
template: "public/index.html",
filename: "index.html"
}),
new vueLoaderPlugin()
]
}
最后 config/webpack.production.js:
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
const vueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
mode: "production",
output: {
path: path.resolve(__dirname, '../dist'),
filename: "bundle.min.js"
},
plugins: [
new htmlWebpackPlugin({
template: "public/index.html",
filename: "index.html"
}),
new vueLoaderPlugin()
]
}
其实就是在原有基础上加入了 vue-loader,这时我们再 yanr serve 就会发现已经可以跑起来 vue 文件了,并且是可以热更新的。
- 现在我们把 vue 文件的扩展名去掉吧!就是在 webpack.config.js 中进行如下操作:
module.exports = env => {
env = env || {};
return {
entry: "./src/main",
module: {
rules: [
{ test: /\.vue$/, use: 'vue-loader' }
]
},
resolve: {
// 默认扩展名
extensions: ['.js', '.json', '.vue'],
// 别名
alias: {
vue: 'vue/dist/vue.esm'
}
},
...(env.production ? require('./config/webpack.production') : require('./config/webpack.development'))
}
}
这里除了 webpack 默认认识的 .js .json 扩展名只增加了 .vue 扩展名,同时起了个别名 vue 就指定到后面的目录
接下来我们就可以把 main.js 中改成如下:
import Vue from "vue";
import App from "./App";
new Vue({
el: "#app",
components: {
App
},
template: `
<div>
<app></app>
</div>
`
})
同时将 App.vue 文件改成如下:
<template>
<div>
<com></com>
</div>
</template>
<script>
import Com from "./components/com";
export default {
name: "App",
components: {
Com,
},
};
</script>
这里只是将这几个文件中 import 的文件扩展名去掉了,同时我将 src/components/com.js 文件改了名字,改成 comm.js 这么做是为了 App.vue 中 import 的 com 是指 com.vue 文件而不是 com.js 文件,因为不加扩展名默认是找到 com.js 文件的
这时再次将我们的项目跑起来,是不是很完美?现在还有一个问题,就是我们可以在 vue 文件中写样式啊,但是现在其实 webpack 还不认识它,所以,盘它!
3.3 css
yarn add -D css-loader
yarn add -D style-loader
同理,我们先装好这哥俩,才能让 webpack 处理 css 代码。
现在我们给我们的 com.vue 随便写点样式:
<template>
<div>
<h1 class="nyya">{{message}}</h1>
</div>
</template>
<script>
export default {
name: "Com",
data() {
return {
message: "我是 Nyya",
};
},
};
</script>
<style scoped>
.nyya {
width: 200px;
height: 300px;
background-color: blanchedalmond;
color: darkseagreen;
font-size: 18px;
text-align: center;
line-height: 300px;
}
</style>
当然我们现在跑是看不到的效果的,因为还没有配置 webpack 啊,如下:
module.exports = env => {
env = env || {};
return {
entry: "./src/main",
module: {
rules: [
{ test: /\.vue$/, use: 'vue-loader' },
// 这里 webpack 使用 loader 是从右向左的
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
},
resolve: {
// 默认扩展名
extensions: ['.js', '.json', '.vue'],
// 别名
alias: {
vue: 'vue/dist/vue.esm'
}
},
...(env.production ? require('./config/webpack.production') : require('./config/webpack.development'))
}
}
这里只是增加了一个对 css 文件处理的 loader,目的是先使用 css-loader 获取到 css 再使用 style-loader 将 css 解析
好,yarn serve !
四、总结
我其实还是有许多疑问的,我也还在学习中,希望有什么说的不到位的或者说错的地方再或者有自己的见解的朋友们可以一起讨论啊。
到这里其实就已经告一段落了,我觉得大家应该都能了解 vue-cli 到底做了些什么了,它其实没有那么神奇,真正厉害的还是 webpack。我在这里贴上项目的地址,感兴趣的“前端魔芋丝”们,可以了解一下啊。