1. 为什么要了解这些
vue 的脚手架集成了 webpack,并进行了大量的默认配置
我们只需要按照框架的要求编写代码,打包就好了
慢慢的我们就成了傻瓜,不知道这其中到底发生了什么,一旦遇到问题,不知道该如何思考,因为你可能都不清楚它的运作原理
更何况,看尤大的思路,将来很可能使用脚手架创建项目时,打包工具不再是默认的,而是让你 webpack 和 vite 二选一
所以,我们就更需要对打包过程有一个大概的了解
下面我们就手动实现这一点
2. 动手实践
SPA 和组件化密不可分,组件化让SPA成为可能,SPA促使组件化编程的落地
vue 的单文件组件就是具体的体现
2.1 搭建目录结构
创建项目文件夹 webpack-demo
运行如下命令进行初始化
npm init -y
根目录下新建 src 目录
src 目录下新建 index.html,main.js
index.html 代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<!-- 引入打包之后的文件,先不用理解 -->
<script src="../dist/bundle.js"></script>
</body>
</html>
- id=app 的div 是将来 vue 实例的挂载点
- bundle.js 是打包生成的文件,先不同管它
src 目录下新建 Home.vue
<template>
<div class="home">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
data: function () {
return {
msg: 'Home'
}
},
methods: {
},
computed: {
},
watch: {
}
}
</script>
<style scoped>
h1 {
color: red;
}
</style>
当前目录结构如下
2.3 安装和配置webpack
安装 webpack
npm i webpack webpack-cli -D
根目录下新建 webpack.config.js,配置打包模式、入口和出口文件
const path = require('path')
module.exports = {
mode: 'development',
entry: path.join(__dirname, 'src', 'main.js'), // 打包的入口文件
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
}
}
package.json 中添加打包命令
2.4 编写打包入口文件
在 src/main.js 中编写代码
import Vue from 'vue'
import Home from './Home.vue'
let vm = new Vue({
el: '#app', // 指定当前vue实例的挂载点,也就是将index.html 中的id=app 的dom作为挂载点
// 将 Home 组件中的模板和样式渲染到 index.html 页面中,具体来说,
// 就是用模板中的内容替换 index.html 中的 id=app 的元素,也会将 css 放到页面中
render: h => h(Home)
})
运行打包命令
npm run dev
报错
错误解决见下一节
2.5 安装相关加载器(loader)
上面错误原因在于 webpack 默认只能打包 js 文件,对于 .vue 文件不认识
所以需要安装能够处理 vue 文件的 loader
这个 loader 不能在 webpack 文档中找,因为 webpack 与 vue 本身是没有关系的
webpack 只是一个打包工具,默认可以处理 js 文件,并且提供了一些通用的 laoder,比如
- css-loader 处理 css 文件的打包工作
- url-loader 处理图片字体等静态资源文件的打包工作
所以,vue 官方提供了能够处理 vue 文件的加载器
https://vue-loader.vuejs.org/zh/
安装
npm install -D vue-loader vue-template-compiler
在webpack 中配置
错误信息
上面错误是因为组件中有 css 代码,需要对应的 loader
安装 loader
npm i style-loader css-loader -D
配置规则
module: {
rules: [
{ test: /\.vue$/, loader: 'vue-loader' },
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
},
重新运行
npm run dev
打包成功
运行 index.html
2.6 多组件如何切换
src 目录下新建 About.vue
<template>
<div class="about">
<h2>关于我们</h2>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
h2 {
color: orange;
}
</style>
希望运行 index.html 的时候展示 About 组件的内容,怎么办?
可以修改 main.js
重新打包
npm run dev
index.html
虽然实现了,但很显然,这种实现方案是不现实的
2.7 使用路由实现组件切换
其实,组件切换的逻辑应该是这样的
- 首先,我们是一个 SPA 应用,只有一个 index.html 作为应用入口
- 传统开发模式中的多页面,此时以多组件的方式实现。不严谨的说,就是传统模式下的一个页面对应这里的一个组件,当然我们这里组件的粒度更精细
- 通过地址栏中的 hash 值的变化,寻找对应的组件在页面中显示
我们都知道,页面实际运行中,其实就是将 vue 实例中 render 属性指定的组件内容替换掉 index.html 中的 id=app 的元素
我们新建一个根组件,将根组件的内容显示到 index.html 中的 id=app 的元素上
当组件发生切换时,改变根组件中的内容,也就是将其他组件的内容显示到跟组件上,而根组件又渲染到 index.html 中,所以最终 html 页面也会变化
引入 vue 的官方路由 vue-router,来实现这一点
安装vue-router
npm install vue-router
创建根组件
src 目录下创建 App.vue
<template>
<div id="app">
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
其中 router-view 作为匹配到路由规则的组件的展示区域
修改打包入口文件
main,js 中引入 vue-router,并编写路由规则,并向 vue 实例注册 router 实例
还要注意,上面 render 函数中将组件名称改成了 App 组件
重新打包
通过修改地址栏中的 hash 值,可以看到对应的组件内容
2.8 公共组件如何处理
我们不能要求用户通过修改地址栏来切换不同的组件
必须要提供一个导航
而且这个导航,无论 Home 还是 About 组件都需要
所以不能分别在两个组件中定义一份,否则随着项目的扩大,维护成本会越来越高
解决方案:提取一个公共导航
src 目录下新建 NavBar.vue
<template>
<nav>
<ul>
<li><router-link to="/home">首页</router-link></li>
<li><router-link to="/about">关于我们</router-link></li>
</ul>
</nav>
</template>
<script>
export default {
}
</script>
<style scoped>
ul {
list-style-type: none;
overflow: hidden;
}
li {
float: left;
padding: 10px;
}
a {
text-decoration: none;
}
</style>
修改 App.vue,引入上面的组件,并在 router-view 上面使用
重新打包,然后运行 index.html
现在我们可以通过点击连接切换组件了
至此,一个简单的 SPA 应用就完成了
2.9 项目目录调整
随着项目变大,组件会越来越多,全部都放在 src 目录下,不便于管理
另外,随着组件的增多,路由规则也会越来越多,我们当前将所有路由规则写在 入口文件 main.js 中,会让文件越来越臃肿,另外入口文件的作用应该是告诉webpack 哪些文件应该打包,而不应该放路由处理这种逻辑代码,所以应该也提取出来
具体操作
- 新建 views 目录,存放页面级组件,如 Home.vue,About.vue
- 新建 components 目录,存放局部组件,如 NavBar.vue
- 新建 router 目录,其下新建 index.js,将路由处理相关代码放在这里
router/index.js 中代码如下
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
// 安装路由功能:
Vue.use(VueRouter)
/**
* 创建路由规则
* 当用户请求不同的hash值时,显示不同的组件(将不同的组件内容显示在 <router-view></router-view>)
*/
const routes = [
{ path: '/home', component: Home },
{ path: '/about', component: About }
]
// 创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({
routes
})
export default router
main.js 中代码修改如下
import Vue from 'vue'
import router from './router' // 可以省略 index.js
import App from './App.vue'
let vm = new Vue({
el: '#app', // 指定当前vue实例的挂载点,也就是将index.html 中的id=app 的dom作为挂载点
// 将 Home 组件中的模板和样式渲染到 index.html 页面中,具体来说,
// 就是用模板中的内容替换 index.html 中的 id=app 的元素,也会将 css 放到页面中
render: h => h(App),
router
})
运行命令,重新打包,发现程序能够正常运行
npm run dev
3. 总结
经过一系列的步骤,我们手动创建的第一个单页面应用已经成功
仔细对比一下,发现与使用 vue 脚手架创建项目,目录结构基本一致了