> 文档中心 > Vue-Loader 打包单文件组件实战

Vue-Loader 打包单文件组件实战


本文是我在学习过程中记录学习的点点滴滴,目的是为了学完之后巩固一下顺便也和大家分享一下,日后忘记了也可以方便快速的复习。

Vue-Loader 打包单文件组件

  • 前言
  • 一、webpack 结合 Vue-Loader 打包单文件组件基本认识
    • 1.1、什么是单文件组件
    • 1.2、什么是 Vue-Loader
    • 1.3、手动配置 webpack 集成 vue-loader
    • 1.4、创建单文件组件
    • 1.5、在 main.js 文件中把 Hello.vue 当做一个模块引入
    • 1.6、查看打包后的代码
    • 1.7、创建单文件组件(.vue)的快捷方法
  • 二、webpack 结合 Vue-Loader 打包单文件组件实战
    • 2.1、项目结构
    • 2.2、下载安装 vue,这里指定版本
    • 2.3、如何引用 vue 文件【编写 main.js 文件代码】
    • 2.4、打包项目 npm start
    • 2.5、运行 index.html
    • 2.6、解决组件内容没有渲染出来
  • 三、持续改进——采用 render 函数渲染组
    • 3.1、总结上面 2 种解决子组件内容没有渲染出来的方案
    • 3.2、改进 main.js
    • 3.3、注释掉 main.js 中的下面的引入完整版的 vue.js 代码
    • 3.4、打包运行测试
    • 3.5、采用箭头函数优化下 render 函数
  • 四、完善改进——丰富 Vue 单文件组件
    • 4.1、新建子组件(也是单文件组件)
    • 4.2、在根组件 App.vue 中引入子组件
    • 4.3、打包浏览测试
    • 4.4、丰富下 App.vue组件
  • 五、webpack 模块热替换(HMR)(局部更新)
    • 5.1、模块热替换
    • 5.2、启用 HRM 步骤
      • 5.2.1、安装 webpack-dev-server
    • 5.3、配置 webpack.config.js
    • 5.4、测试效果

前言

今天学习的主要是关于webpack 结合 Vue-loader 来打包 Vue 单文件组件知识的理解和应用。主要内容包括什么是单文件组件、vue-loader 是什么,单文件组件结构及代码编写,利用 Vue-Loader 打包单文件组件,为什么要采用 render 函数来渲染,webpack 模块热替换(HMR)如何启用及使用。


一、webpack 结合 Vue-Loader 打包单文件组件基本认识

总结:
1、.vue文件是一个个的组件,里面的是被template标签包裹的内容,且template只能有一个子标签,但是可以有无数个孙标签。
2、安装好vue-loader 和 vue-template-compiler两个后需要在webpack.config.js配置相关信息,导入插件、实例化插件、配置规则。
3、需要把vue文件当作模块引入到main.js中然后打包运行。

1.1、什么是单文件组件

引出问题:
前 面 创 建 组 件 通 过 Vue.component ( 全 局 组 件 ) 或
compontents 选项(局部组件),并且写在 html 文件中的 js 部分,小规模项目这样写没问题,但是项目大这样很多组件全部写在一个html 文件中就非常不好。因此我们希望能够把组件内容单独提出来,后续需要使用组件把它作为模块去引入就好。

因 此 , vue 为 我 们 提 供 了 单 文 件 组 件 ( single-file
components):就是一种扩展名为 .vue 的文件,是 vue.js 自定义的一种文件格式,在文件内可以封装组件相关的 html、css、js。也就是说一个 vue 文件由三部分组成:
——html 代码
——css 样式
——js 代码
当然不是三部分必须都有,但 html 代码部分必须有,其他二部分可以没有,也可以有其中一部分。

官方示例:hello.vue
Vue-Loader 打包单文件组件实战但是浏览器能够直接识别扩展名为.vue 的文件吗?
——不能。
需要采用 webpack 对其打包,打包成浏览器能够识别的文件。但是前面讲过 webpack 本身只能对 js 文件进行打包,那怎么办呢?
vue 官网推荐了 Vue-Loader 加载器

1.2、什么是 Vue-Loader

Vue Loader 是一个 webpack 的 loader(加载器),它允许你以一种名为单文件组件 (SFCs)的格式撰写 Vue 组件。
简单说:Vue-Loader 就是官方为 webpack 提供的一个加载器,用来配合 webpack 对单文件组件进行打包(成浏览器能够识别的文件)

1.3、手动配置 webpack 集成 vue-loader

(1)安装 vue-loader 和 vue-template-compiler
vue-template-compiler 是 vue 的编译器,所以要同步安装。
直接复制第 15 章的项目到第 16 章目录下。接着通过 vs 终端安装。即执行如下代码:
npm install -D vue-loader vue-template-compiler
(2)配置 webpack.config.js,在该文件中添加以下 3 处黄色底纹代码,其含义见注释。

//引用 node.js 中的 path 模块,用来处理文件路径
const path = require(“path”);
//引入插件 html-webpack-plugin
const HtmlWebpackPlugin = require(‘html-webpack-plugin’);
//1.导入 vue-loader 插件
const VueLoaderPlugin = require(‘vue-loader/lib/plugin’)

// 导出一个 webpack 具有特殊属性配置的对象
// 安装下 Node Snippets 插件,输入 module 会有智能提示
module.exports = {
mode: ‘none’,//指定打包为生产环境、开发环境或者设置 none
//入口
entry: ‘./src/main.js’,// 入口模块文件路径
//出口对象
output: {
// path 必须是一个绝对路径 , __dirname 是当前配置文件
webpack.config.js 的绝对路径。然后与输出目录 dist 拼接成一个决定路径
path: path.join(__dirname, ‘./dist’),
filename: ‘bundle.js’ },// 配置插件
plugins: [
new HtmlWebpackPlugin({
//指定要打包的模板页面 index.html,采用的是相对路径,与当前配置文件在同级目录,所以为./。就会找到把 index.html 文件并把它打包到与输出文件 bundle.js 的同级目录下
template: ‘./index.html’ }),
// 3.请确保引入这个插件!(实例化插件)
new VueLoaderPlugin()

],//实时重新加载
devServer: {
//在当前目的 dist 目录下查找文件
contentBase: ‘./dist’, },
module: {
rules: [ //配置 css 转 js 的规则
{
test: /.css$/,//正则表达式,匹配以 css 结尾文件
use: [ //下面 2 个加载器的顺序不能反
‘style-loader’,//让 javascript 识别转换后的 js(css)
‘css-loader’ //css 转为 js
]
},{
test: /.(png|svg|jpg|gif)$/, use: [
‘file-loader’ ]
},{//解决兼容性问题
test: /.m?js$/, exclude: /(node_modules)/,//排除 node_modules【是各种插件安
装目录】下的代码不用 babel_loader 去转换
use: {
loader: ‘babel-loader’, options: {
presets: [‘@babel/preset-env’]//babel 中内置的转换规则工
具,刚才配套一起安装的就还有这个
}
}
},{ //2.指定扩展名为.vue 的文件用 vue-loader 加载
test: /.vue$/,
loader: ‘vue-loader’ }

]
}
}

说明:第 3 步实例化 new VueLoaderPlugin() 插件是必须的。它的职责是将你定义过的其它规则复制并应用到 .vue 文件里相应语言的块。
例如,如果你有一条匹配 /.js$/ 的规则,那么它会应用到.vue 文件里的 块。

1.4、创建单文件组件

在 src 文件夹下新建一个单文件组件,比如:Hello.vue ,编写代码如下:

//有且只有一个根元素<template><div><div>欢迎您学习 vue</div></div></template><script>export default {}</script><style></style>

接下来把该单文件组件 Hello.vue 当做一个模块来引入

1.5、在 main.js 文件中把 Hello.vue 当做一个模块引入

把 main.js 原来有的代码先删除。输入下面代码导入Hello.vue文件
import Hello from ‘./Hello.vue’
然后执行下打包:
npm run start
编译没有报错,说明上面的导入语法就没有问题。

1.6、查看打包后的代码

进入打包后的文件bundle.js文件可以看到Hello.vue中的代码被打包成类似下面代码:
return _c(“div”, [_vm._v(“欢迎您学习 vue”)])
这个就是浏览器能够识别的(虽然我们不太认识)。

1.7、创建单文件组件(.vue)的快捷方法

(1)安装插件 Vetur(第 2 章已安装)
(2)配置模 Vetur 板代码片段
依次选择 “文件 ->首选项->用户代码片段”,此时,会弹出
一个搜索框,输入 vue , 默认内容如下:Vue-Loader 打包单文件组件实战把中间注释内容改为如下:

"Print to console": {"prefix": "vue", "body": [ ""," 
"," $0", //指光标出现位置"
"
, "", "", "", "export default {"," data () {"," return {"," }", " },", "", " components: {},","", "}","","", "",""],"description": "Log output to console"}

或者直接打开源代码下面有个 vue.json,复制内容即可。最后保存下即可。
(3)后面创建.vue 文件,创建好空白文件后,输入“vue”回
车或者 Tab 键,即可生成默认模板代码。

二、webpack 结合 Vue-Loader 打包单文件组件实战

总结:
在main中引入vue因为没有确切的指定版本,所以他会默认导入运行时版本(不具有编译功能),我们有两种解决方法,第一种就是指定确切版本,第二种就是在 webpack.config.js 配置好命令,使导入vue就是导入完整版的vue.js。

2.1、项目结构

webpack-demo1|- index.html //单页面入口文件|-src //源文件目录|-main.js //打包入口文件|-App.vue //根组件,替换 index.html 中的 app 处|-router.js //路由配置文件|-components //存放组件的文件夹|-webpack.config.js //webpack 配置文件|-package.json //项目配置文件|-node_modules //安装项目各种依赖的目录

因此把Hello.vue重命名为App.vue;src文件夹下添加router.js文件;src 文件夹下添加 components 文件夹,项目大还可以在components 文件夹下再进行组件分类,建立不同的文件夹。
index.html 文件 body 节中添加:

2.2、下载安装 vue,这里指定版本

npm install vue@2.6.1

2.3、如何引用 vue 文件【编写 main.js 文件代码】

原来我们都是通过下面代码引入 vue。

现在我们只要在 main.js 把 vue 作为一个组件引入即:

// Vue 是自己取的名称,一般 V 大写,后面的 vue 是安装的 vue 组件名称,不能更改。会自动到项目文件夹下的 node_modules 文件夹下去找,找哪个文件?vue.js???——后面告诉答案import Vue from 'vue' //在 main.js 中要把 App.vue 作为其子组件使用,就要把它导入,并取名为 App 。./就表示当前目录import App from './App.vue' //导入 Vue 之后就可以使用 Vue 了new Vue({el:"#app",//app 名称与 index.html 中 id 名称保持一致即可//注册子组件components:{ //上面导入了 App.vue,这里就把它作为components 的一个选项设置即可// App:App 简写为 AppApp},//为 Vue 组件(根组件)设置模板//表示用上面注册的组件 App 替换 index.html 里面的
template:'', //写成 a 大写或小写都可以的})

2.4、打包项目 npm start

之后打开打包后的文件 bundle.js 文件。有 9000 多行代码,把vue 文件内容都打包进去了。
打开打包后的index.htm(l 即是 dist目录下的 index.html 文件),多了下面一句。

2.5、运行 index.html

访问 dist/index.html ,发现App组件没有被渲染出来,按 F12查看控制台发现报警告:
Vue-Loader 打包单文件组件实战警告大意:您使用是 Vue 仅运行时版本,其中模板编译器不可用。要么将模板预编译成呈现函数,要么使用包含编译器的 vue 进行
即是说:原因是因为在 main.js 导入 vue 版本有问题。原先我们引入 vue.js 是如下方式,我们实际发现 vue 是有很多版本,我们原先引入的是 vue.js 是完整版本(具有编译和运行功能)Vue-Loader 打包单文件组件实战这种方法导入的 vue 不是完整版本,不具有编译功能,即不能对 App.vue 进行编译,而我们需要编译成浏览器能够识别的 js 代码,这种方法导入是运行时版本。
也就是说,template 渲染的字符串,运行时版本 vue 无法解
析。
为什么说默认导入的是运行时版本的 vue 呢?因为
import Vue from ‘vue’ 导入的 vue 文件默认是
node_modules\vue\ package.json 中的 main 属性指定的文件,可以发现它并不是我们熟悉的 vue.js 完整版文件,import 的是运行时版本
【 “main”:“dist/vue.runtime.common.js”】,不是完整版。Vue-Loader 打包单文件组件实战但是我们不好对安装的依赖文件夹下的 node_modules\vue
package.json 中的 main 属性进行修改,因为只要后续用户一安装那个 vue 就会覆盖了。那怎么办呢?

2.6、解决组件内容没有渲染出来

因为默认 main.js 文件中导入的不是 vue 完整版,下面提供 2种解决方案:
第 1 种解决方法:
就在 main.js 文件中把导入的 vue 改为完整版本,即如下:
import Vue from ‘vue/dist/vue.js’
这里 vue 自然会找到根目录下的 node_modules 文件夹下的 vue。
然后重新打包 npm start,完了之后可以查看下打包后的
bundle.js 文件发现有 12000 多行,就是因为这时打包的是完整版的vue.js。
最后再重新运行 dist/index.html,发现 App.vue 组件中的内容
渲染出来了。Vue-Loader 打包单文件组件实战但是这种解决方案不是很好的。用的不多
第 2 种解决方案:
(1)main.js 导入 vue 的代码保持不变
import Vue from ‘vue’
(2)在 webpack.config.js 增加一个属性,放在最后一个 } 前即可。
……
, // 去引用完整版 vue.js

resolve:
{
alias:
{ ‘vue$’: ‘vue/dist/vue.js’ }
}
}

重新打包,npm start,打包之后之后可以查看下打包后的
bundle.js 文件发现有 12000 多行,就是因为这时打包的是完整版的vue.js。最后重新运行下 dist/index.html,发现 App.vue 组件中的内容渲染出来了。

三、持续改进——采用 render 函数渲染组

总结:
虽然使用vue.js完成版可以编译,但是它太大了,浪费性能,所以我们依旧使用默认的运行时版本,将编译任务交给 vue-loader 加载器,vue运行时只需要负责渲染功能就行。
之前引入组件都是使用template ,但是template 自
身没有渲染功能,最终渲染底层都是通过 render 函数实现的,但我们又要把 template 属性编译成 render 函数,编译过程有一定的性能损耗。所以可以直接使用运行时版本 vue ,然后直接通过render 函数来渲染组件即可。

3.1、总结上面 2 种解决子组件内容没有渲染出来的方案

(1)以上两种方法都可以解决。但是 vue 完整版比运行时 版本大,性能不如运行时版本 vue。
(2)官方更推荐运行时版本 vue,因为 vue-loader 可以编
译 .vue 文件,所以是不需要 vue 的编译功能的,只需要渲染功能即可。
(3)前面都是通过 template 属性引入组件,而 template 自
身没有渲染功能,最终渲染底层都是通过 render 函数实现的。因此需要把 template 属性编译成 render 函数,这个编译过程有一定的性能损耗。
因此,最佳方案是:
使用运行时版本 vue ,然后直接通过render 函数来渲染组件即可

3.2、改进 main.js

通过 template 属性导入 App 组件,改为直接通过 render 函数渲染。此外,通过 render 函数进行渲染组件,注册组件都可以不需要,因为通过作为 h 函数的参数,默认就是子组件。改动的代码如下最后的render。

// Vue 是自己取的名称,一般 V 大写,后面的 vue 是安装的 vue 组件名称,不能更改//这种方法导入的 vue 不是完整版本,不具有编译功能.因为这 vue 是指安装的 node_modules\vue\ package.json 中的 main 属性执行的vue 版本 dist/vue.runtime.common.js。而这个版本是运行时版本,不具有编译功能,所以会有警告错误import Vue from 'vue' //解决方法 1:手动引入完整版本//import Vue from 'vue/dist/vue.js' //在 main.js 中要把 App.vue 作为其子组件使用,就要把它导入,并取名为 Appimport App from './App.vue' //导入 Vue 之后就可以使用 Vue 了new Vue({el:"#app", /* //注册子组件components:{ //上面导入了 App.vue,这里就把它作为components 的一个选项设置即可// App:App 简写为 AppApp}, *///为 Vue 组件(根组件)设置模板//template 没有编译和渲染功能,编译功能可以使用 vue-loader进行编译//而渲染功能可以通过 render 函数进行,所以在此处只需要指定render 函数渲染组件即可//template:'', // a 大写也可以的render:function(h){ //h 是一个函数,这个函数用于接收要渲染的组件,一般就是根组件(App,也就是 App.vue,该组件中相关依赖的组件都会被渲染)return h(App) //函数返回值就是渲染的结果}})

3.3、注释掉 main.js 中的下面的引入完整版的 vue.js 代码

/* // 去引用完整版 vue.jsresolve:{alias:{ 'vue$': 'vue/dist/vue.js' }} */

3.4、打包运行测试

npm star

浏览 dist/index.htm,结果没问题。

3.5、采用箭头函数优化下 render 函数

(1)上面 render 函数代码,可改为使用箭头函数,并通过$mount 挂载元素,即代码:

new Vue({render: h => h(App), }).$mount('#app')

这时实例化 vue 代码实际上很简单,如下。含义可以这么理解:渲染子组件 App(相对 new Vue 这个 根组件)到 index.html 文件指定的 app 处。(如果要问为什么是渲染到 index.html 文件的 app处,因为在 webpack.config.js 中配置好了要打包的模板文件为main.js,而 main.js 中又引用了 App.vue 及 vue.js,所以实质就是把 main.js 和 App.vue 及 vue 一起打包到 bundle.js 文件中(如果还有引入/依赖了其他组件都会被一起打包的),同时打包后的bundle.js 文件被引入到 index.html 文件中【实际上可以这么理解这些组件和 index.html 组合在一起去了】,而这个时候在 bundel.js 中
指定的 app 就是当前文件 index.html 中的 app)。
强调一点:如果子组件还引入其他组件(即是组件依赖的组件),那么打包时会层层打包到各个依赖组件(根据依赖关系都会打包)。

四、完善改进——丰富 Vue 单文件组件

总结:
1、src下只能存在一个根组件,其余的组件都写在components 里面。
2、在根组件引入子组件且.vue后缀不能省:
import Childapp from “./components/Childapp.vue”;
export default {
//2.注册子组件
components: {
Childapp
}}
引入后还要在export default 中定义该子组件,default 指的就是这个根组件自身这组件。
使用子组件的话和
都可以
3、里面的scoped属性是指该样式是否应用到全局的意思,使用了这个属性可以使父子组件的样式不会互相影响。

上面例子中只有一个根文件组件 App.vue

4.1、新建子组件(也是单文件组件)

在 components 文件夹下面添加单文件组件 Childapp.vue,输入内容如下:

<template><div><h3>我是 App 的子组件</h3></div></template><script></script><style></style>

4.2、在根组件 App.vue 中引入子组件

<template><div><div>欢迎您学习 vue!!!</div><!--  --><!--  --><Childapp /></div></template><script>//1.要使用某个组件,需要先导入,然后再使用。扩展名.vue 不能少import Childapp from "./components/Childapp.vue";export default {//2.注册子组件components: {Childapp}};</script><style></style>

4.3、打包浏览测试

npm start
或者
npm run dev(以服务方式运行),建议这种方式,更改 App.vue等组件内容,浏览器自动更新内容。
Vue-Loader 打包单文件组件实战运行成功!
如果 npm run dev 报如下错误:
Vue-Loader 打包单文件组件实战可能原因是端口被占用了

4.4、丰富下 App.vue组件

<template><div><div>欢迎您学习 vue!!!</div><!--  --><!--  --><Childapp />{{msg}}</div></template><script>//要使用某个组件,需要先导入,然后再使用import Childapp from "./components/Childapp.vue";//导出一个默认成员对象,它就是当前组件对象,可以直接在对象中使用 Vue 的选项,data、methods、components、watch、钩子函数等,template 选项不需要,因为 template 选项的内容就是上面标签的内容export default {//注册子组件components: {Childapp},data(){return {msg:"徐照兴欢迎您!!"}}};</script><style></style>

打开浏览器查看,即发现多了 msg 内容“徐照兴欢迎您
注意:组件的 template 标签下都要有且只有一个根元素(一般 就用 div),否则可能会导致 scoped 属性失效。 作用:在 Vue 文件中的 style 标签上有一个特殊的属性,scoped。 当一个 style 标签拥有 scoped 属性时候,它的 css 样式只能用于当 前的Vue 组件,可以使组件的样式不相互污染

五、webpack 模块热替换(HMR)(局部更新)

该部分内容只是对开发人员有用,可以实时局部自动更新,可以提高开发效率。

参考官网:
https://www.webpackjs.com/guides/hot-module-replacement/

5.1、模块热替换

模块热替换(Hot Module Replacement 或 HMR)是
webpack 提供的最有用的功能之一。它允许在运行时更新各种模块,而无需进行完全刷新【即可以做到局部刷新】。
HMR 不适用于生产环境,这意味着它应当只在开发环境使用。与上一章讲的使用 webpack-dev-server 插件实现自动打包和刷新页面的区别是:使用 webpack-dev-server 实现刷新页面是当有内容修改时会刷新整个页面。而模块热替换是局部刷新内容更改处。

5.2、启用 HRM 步骤

5.2.1、安装 webpack-dev-server

本项目已安装(安装在前面讲过)

5.3、配置 webpack.config.js

三个步骤导入 webpack、启动模块热替换、实例化模块热替换插件,如下黄色底纹代码:
//(1)导入 webpack,模块热替换需要
const webpack = require(‘webpack’);
// 配置插件
plugins: [
new HtmlWebpackPlugin({
//指定要打包的模板页面 index.html,采用的是相对路径,与当
前配置文件在同级目录,所以为./。就会找到把 index.html 文件并把它
打包到与输出文件 bundle.js 的同级目录下
template: ‘./index.html’ }), // 3.请确保引入这个插件!
new VueLoaderPlugin(),
//(3)实例化模块热替换插件
new webpack.HotModuleReplacementPlugin()

],//实时重新加载
devServer: {
//在当前目的 dist 目录下查找文件
contentBase: ‘./dist’,
hot: true //(2)开启模块热替换
},

5.4、测试效果

说明:这个测试启用只能用 npm run dev
因为 dev 才是通过 webpack-dev-server 去启用服务,这个时候采用使用模块热加载。见 package.json 中配置:
“dev”: “webpack-dev-server --open”
假设修改下 Childapp.vue 或 App.vue 组件的内容,保存下,就会自动重新编译,稍等一会就看到更新后的页面内容,但是并不是整个页面刷新。注意观察:刷新按钮不会转动。【要接收下原来的运行重新执行 npm run dev】

模块热替换只是针对模块(单文件组件),如果更新了
index.html 或 js 文件(main.js)并不会立即更新,需要重新打包后重新浏览才会看到修改后的结果。

💕 原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下}

👍 点赞,你的认可是我创作的动力! \textcolor{orange}{点赞,你的认可是我创作的动力!}

收藏,你的青睐是我努力的方向! \textcolor{red}{收藏,你的青睐是我努力的方向!}

🥕 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!}