开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情
面试总是问 Webpack 怎么样?有什么用?有几个 loader,有几个 plugin,结果一去写业务,全都是上的 Vue CLI,create-react-app,启动配置什么的,前辈们都配好了
所以这篇文章就是教学如何基于 Webpack 从 0 到 1 启动一个 Vue 项目,下面是这个项目运行效果
传统启动
如果你是刚开始接触 HTML/CSS/JavaScript 三件套开始接触的前端,那么你可能比较熟悉或者比较能接受的引入 Vue 的方式可能是使用 CDN 的方式,大概如下(下面这个是我要介绍的例子)
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<body>
<div id="app">
<div>
{{ "count:" + count }}
<button @click="handleClick">click + 1</button>
</div>
</div>
</body>
<script>
new Vue({
data() {
return {
count: 0,
}
},
methods: {
handleClick() {
this.count++;
},
}
}).$mount('#app');
</script>
但如果你继续深入学习的话或者看过一些学习视频,往往他们会推荐你去使用 Vue CLI 去开发 Vue 项目,因为具有一定规模的项目是需要这类脚手架的,它集成了很多功能,但是如果让你自己扔掉 Vue CLI 你能否能够在浏览器中运行呢?因为究其本质,Vue CLI 是基于 node 和 npm 运作的,接下来就教你们一步步简单运行一个 Vue 项目
Webpack 启动
npm 初始化
先找个位置并在终端(或者命令行)初始化一个项目
npm init
初始化后会有一些选项,可以直接回车全部忽略,也可以根据自己意向填写
选择完成之后
这个时候系统会创建一个 package.json 文件
接下来要做的就是安装 Vue
# 文章使用的 Vue2 运行
npm install vue@2.7.10
然后你的 package.json 会有多出一个 dependencies 属性,如下
{
"dependencies": {
"vue": "^2.7.10"
}
}
所以上面的例子需要改成如下写法
// <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
import Vue from "vue"
但这种写法在 HTML 文件需要使用 module 加持,而且现在的 Vue 的位置在 node_modules,不能再从 HTML 中直接导入,所以需要将上面例子中的 JS 代码脱离,命名为 main.js
import Vue from "vue";
new Vue({
data() {
return {
count: 0,
}
},
methods: {
handleClick() {
this.count++;
},
}
}).$mount('#app');
html 部分命名为 index.html
<body>
<div id="app">
<div>
{{ "count:" + count }}
<button @click="handleClick">click + 1</button>
</div>
</div>
</body>
<script type="text/javascript" src="./main.js" charset="utf-8"></script>
PS: 为什么脚本引入放在了下面?是因为 main.js 里面包含了获取 DOM 的代码,所以为了能够获取到 <div id="app">...</div> 必须放在其下面导入
此时的文件结构
├── node_modules
├── main.js
├── index.html
├── package-lock.json
└── package.json
如果你有过 HTML 部署的经验或者你的电脑上恰好有 Live Server 这个软件,你会发现这个文件结构已经能够运行
但是效果和预期不一样,会是下面这样
为什么显示是 {{"count:" + count}} 而没有被解析呢?
这是因为在 js 中默认导出的 Vue.js 是只包含运行时而不包含编译时的,而编译时干的就是解析的活,具体可以参考这个文档,对不同构建版本的解释 - Vue.js
像 CDN 使用的就是 UMD 完整版的 Vue.js,而 js 中导出的是只包含运行时的 Vue.js
对于这个问题可以使用 Vue 提供的 h() 渲染函数,像下面这样
new Vue({
// ...
render(h) {
return h("div", [
"count:" + this.count,
h(
"button",
{
on: {
click: this.handleClick,
},
},
"click + 1"
),
]);
},
// ...
}).$mount("#app");
等同于下面的 HTML
<div>
{{ "count:" + count }}
<button @click="handleClick">click + 1</button>
</div>
然后在 package.json 和 html 的导入方式中设置为 module,也可以生成同样的效果,具体详情就不多演示,因为这涉及到另一个前端工程化的大方向,也就是 Vite 类型的打包框架
当然你也使用 vue-loader,但是使用 vue-loader 的前提需要一个前提,即 Webpack
Webpack
执行
npm install webpack webpack-dev-server webpack-cli --save-dev
webpack是一个打包器,可以将不同模块的.js文件打包成一个.js文件,就比如上面的Vue.js和main.js合并成一个.js文件webpack-dev-server可以监视.js文件内容的修改,进行热更新,具体场景比如更新DOM的内容,然后更新到浏览器webpack-cli可以在终端执行webpack和webpack-dev-server
webpack 重点在于它的配置,配置如下
const webpack = require("webpack");
const path = require("path");
module.exports = {
mode: "development",
devServer: {
static: {
directory: path.join(__dirname, "public"),
},
compress: true,
port: 8080,
},
entry: path.join(__dirname, "./main.js"),
output: {
publicPath: "",
path: path.join(__dirname, "./public"),
filename: "bundle.js",
clean: true,
},
};
我认为 Webpack 的配置是一定要讲的,因为有非常多的属性有默认值,关键是它还比较重要!!
我强烈建议不要随便百度你遇到的问题,请去查看 Webpack 文档,即使它有点烂
解析如下
mode是webpack的运行配置entry是打包的入口文件,这里默认为main.jsoutput是打包的出口文件,也就是打包结果path是打包文件的存放位置,由于我们目的是运行Vue项目,目的不在打包而是运行,所以不需要过多关注publicPath需要重点关注,和webpack-dev-server紧密相连,这个属性目的在于配置使用webpack-dev-server启动时打包文件的引用位置
devServer是webpack-dev-server的配置选项,等同于Live Serverport启动一个服务在localhost:port,你可以直接通过http://localhost:port访问热更新的内容staticdirectory是配置静态文件展示的位置的选项,可以简单理解可以直接在浏览器访问在这个位置的index.html,如上就是http://localhost:8080
配置完 Webpack,项目的文件也需要重新调整
index.html需要迁移至public- 里面的
.js文件需要改成打包后的文件,即如下
<body>
<!-- ... -->
</body>
<script type="text/javascript" src="./bundle.js" charset="utf-8"></script>
然后还需要修改一下 package.json 的内容,修改如下
{
"name": "vue-start-with-webpack",
"version": "1.0.0",
"description": "运行 vue 项目 demo",
"main": "main.js",
"scripts": {
"start": "webpack server", // 启动 webpack-dev-server
},
"author": "poplink",
"license": "MIT",
"dependencies": {
"vue": "^2.7.10"
},
"devDependencies": {
"webpack": "^5.75.0",
"webpack-cli": "^5.0.0",
"webpack-dev-server": "^4.11.1"
}
}
现在的目录结构如下
├── node_modules
├── public
| └── index.html
├── main.js
├── index.html
├── package-lock.json
├── package.json
└── webpack.config.js
测试运行 npm run start,结果如下
解析如下
mode是webpack的运行配置entry是打包的入口文件,这里默认为main.jsoutput是打包的出口文件,也就是打包结果path是打包文件的存放位置,由于我们目的是运行Vue项目,目的不在打包而是运行,所以不需要过多关注publicPath需要重点关注,和webpack-dev-server紧密相连,这个属性目的在于配置使用webpack-dev-server启动时打包文件的引用位置
devServer是webpack-dev-server的配置选项,等同于Live Serverport启动一个服务在localhost:port,你可以直接通过http://localhost:port访问热更新的内容staticdirectory是配置静态文件展示的位置的选项,可以简单理解可以直接在浏览器访问在这个位置的index.html,如上就是http://localhost:8080
配置完 Webpack,项目的文件也需要重新调整
index.html需要迁移至public- 里面的
.js文件需要改成打包后的文件,即如下
<body>
<!-- ... -->
</body>
<script type="text/javascript" src="./bundle.js" charset="utf-8"></script>
然后还需要修改一下 package.json 的内容,修改如下
{
"name": "vue-start-with-webpack",
"version": "1.0.0",
"description": "运行 vue 项目 demo",
"main": "main.js",
"scripts": {
"start": "webpack server", // 启动 webpack-dev-server
},
"author": "poplink",
"license": "MIT",
"dependencies": {
"vue": "^2.7.10"
},
"devDependencies": {
"webpack": "^5.75.0",
"webpack-cli": "^5.0.0",
"webpack-dev-server": "^4.11.1"
}
}
现在的目录结构如下
├── node_modules
├── public
| └── index.html
├── main.js
├── index.html
├── package-lock.json
├── package.json
└── webpack.config.js
测试运行 npm run start,结果如下
注意,这个时候你可以观察一下目录里面并没有生成 bundle.js,但 webpack-dev-server 并不是没有打包,而是放在了内存里,只是你没看见,具体可以参考文档
Warning webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,则可以通过 dev server 配置中的
devMiddleware.publicPath选项进行修改。
这也就是为什么需要强调 publicPath 的重要性,它就是指定了 bundle.js 的引用位置
对于值的取舍可以参考 Webpack 文档
vue-loader 引入
先安装 vue-loader
# 注意:vue-loader@15.9.2 适配 Vue2,不要安装最新版,有坑,运行不起来
# 注意:vue-template-compiler 需要和你的 Vue2 版本一致!
npm install vue-loader@15.9.2 vue-template-compiler@2.7.10 --save-dev
在 webpack.config.js 中添加
const { VueLoaderPlugin } = require("vue-loader");
module.exports = {
// ...
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
}
],
},
plugins: [new VueLoaderPlugin()],
};
但到这里其实还没有结束,因为我们的 vue-loader 是针对 .vue 文件的,所以需要将 dom 相关的内容迁移至 .vue 文件里
在 main.js 同目录下创建 App.vue 文件,内容如下
<template>
<div>
{{ "count:" + count }}
<button @click="handleClick">click + 1</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0,
};
},
methods: {
handleClick() {
this.count++;
},
},
};
</script>
<style></style>
main.js 的内容修改为
import Vue from "vue";
import App from "./App.vue";
new Vue({
render: (h) => h(App),
}).$mount("#app");
index.html 的内容修改为
<body>
<div id="app"></div>
</body>
<script type="text/javascript" src="bundle.js" charset="utf-8"></script>
再次运行 npm run start 结果如下
总结
.js 到 .vue 的操作就是这么简单,但好像也没用几个 loader
注意,这个时候你可以观察一下目录里面并没有生成 bundle.js,但 webpack-dev-server 并不是没有打包,而是放在了内存里,只是你没看见,具体可以参考文档
Warning webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,则可以通过 dev server 配置中的
devMiddleware.publicPath选项进行修改。
这也就是为什么需要强调 publicPath 的重要性,它就是指定了 bundle.js 的引用位置
对于值的取舍可以参考 Webpack 文档
vue-loader 引入
先安装 vue-loader
# 注意:vue-loader@15.9.2 适配 Vue2,不要安装最新版,有坑,运行不起来
# 注意:vue-template-compiler 需要和你的 Vue2 版本一致!
npm install vue-loader@15.9.2 vue-template-compiler@2.7.10 --save-dev
在 webpack.config.js 中添加
const { VueLoaderPlugin } = require("vue-loader");
module.exports = {
// ...
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
}
],
},
plugins: [new VueLoaderPlugin()],
};
但到这里其实还没有结束,因为我们的 vue-loader 是针对 .vue 文件的,所以需要将 dom 相关的内容迁移至 .vue 文件里
在 main.js 同目录下创建 App.vue 文件,内容如下
<template>
<div>
{{ "count:" + count }}
<button @click="handleClick">click + 1</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0,
};
},
methods: {
handleClick() {
this.count++;
},
},
};
</script>
<style></style>
main.js 的内容修改为
import Vue from "vue";
import App from "./App.vue";
new Vue({
render: (h) => h(App),
}).$mount("#app");
index.html 的内容修改为
<body>
<div id="app"></div>
</body>
<script type="text/javascript" src="bundle.js" charset="utf-8"></script>
再次运行 npm run start 结果如下
最后检查一下热更新
最后的文件目录如下,以及 github 的例子请参考 pandoralink/vue-start-with-webpack - github
├── node_modules
├── public
| └── index.html
├── App.vue
├── main.js
├── package-lock.json
├── package.json
└── webpack.config.js
总结
刚刚接触 Vue CLI 的同学可以对比一下 Vue CLI 创建的项目和本篇文章的文件目录对应的文件比较,对应 Vue CLI 部分原理
_PS: .js 到 .vue 的操作就是这么简单,但好像也没用几个 `loader