Vue框架
Vue3基础: 前端工程化,webpack,plugin、loader、Source Map
Vue框架是当前前端最火的框架,这里为了后期配合springBoot框架完成一个完整项目的搭建,之前已经分享过学习Vue的铺垫的知识比如ES6模块化,Promise,EventLoop事件循环[消息队列],宏任务和微任务;并且安装了Vue的工具node装载在HBuilderX中
对于Vue框架,我们首先就要了解前端工程化和webpack,webpack之前已经下载到了global中
之前没有接触前端的时候,或许很多人都觉得只要会Html + CSS + JavaScript就可以进行开发,利用bootStrap和JQuery就来美化页面和发送Ajax请求,利用art + template模板引擎来进行模板结构的渲染; 但是实际上 : 前端开发要遵守:
前端工程化 : 企业级的前端项目开发中,把前端开发所使用的工具,技术,流程,经验等进行规范化、标准化、最终实现前面的几个标准
前端工程化能够让前端开发自成体系,覆盖前端项目从创建到部署的方方面面; 最大程度提高了前端的开发效率,降低了技术选型和前后端的协调沟通的成本
目前主流的解决方案就是webpack和parcel,这里主要简单介绍webpack
webpack就是前端工程化的具体的解决方案
提供优化的前端工程化开发支持,代码压缩、处理浏览器端JavaScript的兼容性和性能优化;让工作重心放在功能的实现上,提高了可维护性【后端的Maven、Spring等都可以减少工作量】,项目大部分都是基于webpack进行打包构建
{
"name": "webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
新建src源代码目录
新建src-> index.html首页和src-> index.js脚本文件
<html>
<head>
<meta charset="utf-8">
<title>title>
<script src="./index.js" type="text/javascript" charset="utf-8">script>
head>
<body>
<ul>
<li>这是第1个lili>
<li>这是第2个lili>
<li>这是第3个lili>
<li>这是第4个lili>
<li>这是第5个lili>
ul>
body>
html>
-----------------使用npm命令引入jquery-------------------操作上面的dom
初始化首页基本的结构
运行npm install jquery -S,安装JQuery
PS D:\HBuilderX\Web项目\webpack> npm i jquery -S
added 1 package in 2s
导入包之后,项目中用一个node_modules来存放下载导入的包,这样就可以使用默认导入在其他js文件中使用该目录下面的包【这样js脚本间就不需要像最初那样使用script标签来链入】
//导入JQuery
import $ from 'jquery'
//实现隔行变色
$(function(){ //自动为页面加载完毕之后执行 -- 相当于load事件
//选择器选择标签进行修改属性,odd代表基数,enen都属--过滤
$('li:odd').css('backgroundColor','red')
$('li:even').css('backgroundColor','green')
})
运行到浏览器会报错 : Uncaught SyntaxError: Cannot use import statement outside a module ;这是因为浏览器不兼容 ;而webpack就可以解决这些兼容性问题 — 自动将有兼容性问题的代码转化为没有兼容性的代码,然后运行到浏览器
给项目安装webpack使用命令安装两个包即可,只是开发时使用,所以是-D
npm i webpack@5.5.1 webpack-cli@4.2.0 -D
PS D:\HBuilderX\Web项目\webpack> npm i webpack@5.5.1 webpack-cli@4.2.0 -D
added 136 packages in 6s
//这里就简单声明配置对象
module.exports ={
mode: 'development' //mode值有development和production,开发阶段就用dev
}
npm run XX
来运行脚本 ; npm run dev ; 这里的dev就是随意的,但是值就是webpack ;运行dev脚本就会启动webpack,然后读取webpack.config.js来读取配置项,对项目进行打包等处理 "scripts": {
//"test": "echo Error: no test specified && exit 1" , //这里的只是测试的,没有具体用处
"dev" : "webpack"
},
运行npm run dev之后,会生成dist目录,下面的main.js就是webpack解决之前index.js的兼容性后生成的新的js文件,重新引入
上面的test最好删掉,因为有的时候可能报错
//这里之前出现小问题
[webpack-cli] Error [ERR_REQUIRE_ESM]: require() of ES Module D:\HBuilderX\Web项目\webpack\webpack.config.js from D:\HBuilderX\Web项目\webpack\node_modules\webpack-cli\lib\groups\resolveConfig.js not supported.
这里是因为之前加入了type : module ; 这里要使用require(),这是CommonJS,所以要删掉
可以截取一小段main.js看一下
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => module['default'] :
/******/ () => module;
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
这里已经对所有的js文件进行了处理,并且使用了require,直接将这个文件链入index.html就可以解决兼容性问题
<script src="../dist/main.js" type="text/javascript" charset="utf-8">script>
当修改js文件之后,如果没有热更新,就需要重新运行npm run dev命令,来执行webpack
webpack.config.js是webpack的配置文件,webpack在真正开始打包构建之后,会先读取这个配置文件,从而基于给定的配置,对项目进行打包; webpack是基于node.js开发的工具,所以配置文件,支持使用node.js的语法来进行个性化配置【CommonJS】
表示的是开发环境,不会对打包生成的文件进行代码压缩和性能优化, 所以打包的速度快, 适合在开发阶段使用
生产环境, 会对打包生成的文件进行代码压缩和性能优化, 所以打包速度要慢一点, 适合项目开发完成之后使用
webpack中默认的打包入口文件时src -> index.js
默认的输出文件路径为dist-> main.js
想要修改默认值,需要在webpack.config.js配置文件中进行配置,这里使用的node的语法CommonJS来引入path包: entry指代的时打包入口,output为打包的出口,path和filename分别指定出口的路径和名称
const path = require('path') //CommonJS引入path包
module.exports ={
mode: 'development',//mode值有development和production,开发阶段就用dev
entry: path.join(__dirname,'./src/index.js'),
output: {
path: path.join(__dirname,'./dist'),
filename: 'boundle.js'
}
}
使用的path.join进行路径的拼接 【 因为这里是相对路径】
这里将之前的dist删除之后重新执行命令,发现修改成功
上面每次修改代码之后,都要重新运行npm run dev来执行webpack,可以使用plugin来进行热更新,通过安装和配置第三方的插件,可以拓展webpack的能力
webpack-dev-server
类似于node.js中的命令nodemon,热更新; 每当修改了源代码,webpack会自动对项目进行打包和构建
【-D代表将其记录到开发结点下】,运行命令npm i [email protected] -D
, 下载之后就需要配置到项目中
配置 : 修改package.json-> scripts中的dev命令,之前是webpack,直接执行并不会进行热更新
"dev": "webpack serve"
//修改之后运行npm run dev就可以通过浏览器访问实时的更新,和nodemon类似,如果报错,使用npm i webpack-cli -D 更新之后再使用即可
---------------------result-----------------------
PS D:\HBuilderX\Web项目\webpack> npm run dev
> webpack@1.0.0 dev
> webpack serve
(node:552) [DEP0111] DeprecationWarning: Access to process.binding('http_parser') is deprecated.
(Use `node --trace-deprecation ...` to show where the warning was created)
× 「wds」: Error: listen EACCES: permission denied 127.0.0.1:8080 ---- netstat杀死即可
at Server.setupListenHandle [as _listen2] (node:net:1313:21)
at listenInCluster (node:net:1378:12)
at GetAddrInfoReqWrap.doListen [as callback] (node:net:1516:7)
at GetAddrInfoReqWrap.onlookup [as oncomplete] (node:dns:73:8) {
code: 'EACCES',
errno: -4092,
syscall: 'listen',
address: '127.0.0.1',
port: 8080
}
需要注意的是: webpack-server会启动一批个实时打包的http服务器,所以查看打包效果在http://localhost:8080; 这里和之前的nodemon挂载服务器是一样的效果; 这里可能存在端口号被占用的情况,这里可以先关掉
打开这个网址之后,出现的是项目的目录结构,包括dist等,所以点击src就可以运行index.html; 但是当直接进行修改之后,浏览器中并没有马上改变,这是由于配置插件后打包的文件是到缓存中
打包文件的去向
webpack-dev-server生成到内存中的文件,默认放到项目的根目录中,并且是虚拟的,不可见的
, 可以8080/bundle.js查看到一个文件
<script src="/boundle.js" type="text/javascript" charset="utf-8">script>
这样保存就会自动更新页面nodemon
html-webpack-plugin
webpack中的HTML插件,类似于一个模板引擎插件; 可以通过此插件自定制index.html的内容
安装 : 使用命令npm i html-webpack-plugin -D
这样就可以直接安装到最新的版本
配置 : 打开webpack.config.js
//1.使用commonJS语法导入html-webpack-plugin包
const HtmlPlugin = require('html-webpack-plugin')
//2.创建html插件的实例对象
const htmlPlugin = new HtmlPlugin({
template: './src/index.html',//指定源文件的存放路径
filename: './idex.html' //指定生成文件的存放路径
})
//在module.exports中,加入结点plugins
plugins: [htmlPlugin] //通过plugin结点,让HtmlPlugin生效
实例 :通过html-webpack-plugin插件,将src下面的index.html首页复制一份到根目录中,通过template和filename就可以 将index.html复制一份到根目录下【浏览器内存】 HTML插件复制到根目录的idex.html页面,放入的是内存; 并且页面的底部自动注入了打包的bundle.js文件, < script defer src=“boundle.js”>< /script>所以就不需要再手动注入了
开启实时打包之后,加入html插件,可以删除dist目录,因为不再依赖其下面的bundle.js文件了,而是临时的根目录中的虚拟文件
【通过按两次CTRL + C就可以结束nodemon命令,cls命令可以清屏】
devserver结点
在webpack.config.js配置文件中,可以通过devServer结点对webpack-dev-server插件进行更多的配置,open指定是否自动打开,host和port指定端口号; 配置结点和之前的module,output,entry,plugins平级
devServer:{
open: true, //代表是否自动打开
host: '127.0.0.1', //这里是字符串形式
port:8090 //这样就不会出现占用
}
这里的设置和之前的ES6实例中的nodemon配置服务器相同; 这里需要重新配置服务器
在实际的开发中,webpack默认只能打包处理.js后缀名的模块,其他的非.js结尾的模块如.css不能处理,这个时候,就要使用loader加载器来进行打包
loader加载器的作用 : 协助webpack打包处理指定的文件模块
loader的调用过程:
先判断待处理的模块,如果为js,判断是否有高级的JS语法,没有就直接打包处理,否则就判断是否安装配置相关的loader
在webpack中,一切皆模块; 所以js中可以导入任何的模块比如css等
这里可以继续操作上面的index.html,为其加上样式,在src目录下再创建css文件夹,
其中的index.html 【去掉列表前面的*】
ul:{ ----> 这是错误的写法,选择器后面应该直接跟{},加上: 在firefox中会警告
list-style: none;
}
--------------直接就是选择器加上{样式}---------------
ul{
list-style: none;
}
要让这个css文件生效,按照之前,就会link链入,现在按照模块化思维,直接导入到index.js中即可
import './css/index.css' //只是执行,不使用,所以直接加路径
npm i style-loader css-loader -D
const path = require('path') //CommonJS引入path包
//1.使用commonJS语法导入html-webpack-plugin包
const HtmlPlugin = require('html-webpack-plugin')
//2.创建html插件的实例对象
const htmlPlugin = new HtmlPlugin({
template: './src/index.html',//指定源文件的存放路径
filename: './index.html' //指定生成文件的存放路径
})
module.exports ={
mode: 'development',//mode值有development和production,开发阶段就用dev
entry: path.join(__dirname,'./src/index.js'),
output: {
path: path.join(__dirname,'./dist'),
filename: 'boundle.js'
},
plugins: [htmlPlugin], //通过plugin结点,让HtmlPlugin生效
devServer:{
open: true, //代表是否自动打开
host: '127.0.0.1',
port:8090 //这样就不会出现占用
},
module:{ //所有第三方文件模块的匹配规则标识
rules:[ //文件后缀名的匹配规则
{test:/\.css/,use:['style.loader','css.loader']} //test为正则,文件正则;use为使用的加载器
]
}
}
重启服务器,运行即可打包
这里是因为我们安装了高版本的webpack,所以配置的语法已经不是之前的,在webpack4.xx以后,配置loader必须要用对象的方式指明loader,即 use:[{loader:xxx},……]
module:{ //所有第三方文件模块的匹配规则标识
rules:[ //文件后缀名的匹配规则
{ test: /\.css/, use: [{ loader: 'style-loader' }, { loader: 'css-loader' }] } //test为正则,文件正则;use为使用的加载器
]
}
这里就是要注意,loader前面必须加上loader:
less文件可以操作margin和padding等位置,所以这里就美化一下之前的列表,在css下面新建一个index.less文件
html,body,ul{
margin: 0;
padding: 0;
li{
line-height: 35px;
padding-left: 10px;
font-size: 12px;
}
}
之后在index.js中导入这个模块 import ‘./css/index.less’,要想打包处理,还是需要安装配置两个loader
安装 : npm i less-loader less -D
配置 : { test: /.less$/,use: [{ loader: ‘style-loader’ }, { loader: ‘css-loader’ },{loader:‘less-loader’}]}//不用less,less是less-loader的内置依赖项,而是要用style和css
现在在index上面新建一个id为box的div,目的是将src/img/local.png作为该box的背景图片
正常来说,在index.less下面,会对这个div进行样式的编辑
#box{
width: 380px;
height: 114px;
background-color: yellow;
background: url(../img/local.png);
}
保存之后,也还是报错了: You may need an appropriate loader to handle this file type, 这是因为不能处理图片文件.jpg,.png结尾的文件
安装: npm i url-loader file-loader -D
安装两个loader
配置 : { test: /\.jpg|png|gif$/,use:[{loader: 'url-loader'}]} //暂时就只配置url-loader,图片各种后缀使用|
之后重新运行即可:但是div的背景是截取的图片的一部分…
url-loader参数limit
网页中,小的图片可以直接转化为base64直接加载,大型图片再发请求,这样可以提高网页访问的性能,而选择大小就需要url-loader的参数limit ,小于等于的就会转为base64,limit是用来指定图片的大小,单位为byte
{ test: /\.jpg|png|gif$/,use:[{loader: 'url-loader?limit=207946'}]
设置之后可以重新查看图片 : url(data:image/png;base64 —> 被转为了base64
loader配置可以采用对象的方式进行配置:
use:{
loader:'url-loader' , //loader属性指向调用的loader
options:{
limit:2229 //通过options指定参数项
}
}
webpack只能打包处理一部分高级的js语法,对于webpack无法处理的js高级语法,只能使用babel-loader来协助打包;最新版本的webpack可以处理
class Person{
//使用static为Person类声明一个静态属性
//webpack无法处理这个static的高级语法,需要使用babel-loader
static info = 'person info';
}
babel-loader含有三个主要的包babel-loader,@babel/core,@babel/plugin-proposal-class-properties
安装 : npm i babel-loader @babel/core @babel/plugin-proposal-class-properties -D
配置 : 需要注意,除了test和use,还有一个exclude : 排除项 :‘node_modules’;babel-loader只需要处理用户编写的js文件,不需要处理node_modules下面的下载的其他的包文件,这样速度快
{
test: /\.js$/,
exclude:/node_modules/ , //这里同样是正则
use:{//要声明插件,用来转化高级语法,后面的@开头的都是插件
loader: 'babel-loader',
options:{
plugins:['@babel/plugin-proposal-class-properties']
}
}
}
上面的都是rules数组的一项
项目完成之后,使用webpack对项目进行打包发布 :
之前再package.json下面的scripts结点下配置的是dev命令,代表的就是development,当项目完成之后,重新配置命令build
'build': 'webpack --mode production' //没有serve,所以没有使用server插件,直接放到了物理的磁盘上,同时会修改webpack.config.js的mode为production,就会压缩
这里会覆盖config中的mode的值
这样使用命令npm run build
直接生成文件杂乱,要将js文件统一生成到js目录中,需要再output中进行配置
//生成的位置filename,只要加上js级即可
output: {
path: path.join(__dirname,'./dist'),
filename: 'js/boundle.js'
}
对于图片文件,生成到img目录下,需要对url-loader进行配置,除了原来的limit参数,新增一个outputPath参数指定路径
{ test: /\.jpg|png|gif$/,use:{
loader: 'url-loader',
options:{
limit:227946,
outputPath:'image'
}
}}
为了每次打包发布自动清理dist目录,安装配置插件clean-webpack-plugin
npm i XX -D
安装配置这个插件和之前配置html插件是相同的
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const cleanPlugin = new CleanWebpackPlugin()
//1.使用commonJS语法导入html-webpack-plugin包
const HtmlPlugin = require('html-webpack-plugin')
//2.创建html插件的实例对象
const htmlPlugin = new HtmlPlugin({
template: './src/index.html',//指定源文件的存放路径
filename: './index.html' //指定生成文件的存放路径
})
module.exports ={
mode: 'development',//mode值有development和production,开发阶段就用dev
entry: path.join(__dirname,'./src/index.js'),
output: {
path: path.join(__dirname,'./dist'),
filename: 'js/boundle.js'
},
plugins: [htmlPlugin,cleanPlugin], //通过plugin结点,让HtmlPlugin生效
在高级的打包发布中,包括打包生成报告,分析具体的优化法案,Tree-Shaking,为第三方库提供CDN加载,配置组件的按需加载,开启路由懒加载,自定制首页的内容
生产环境中遇到的问题: 前端项目投入生产环境后,都需要对js文件进行压缩混淆,从而减小体积,提高加载效率,但是产生问题 : 压缩后的代码不好debug
Source Map是一个信息文件,里面存储这位置信息, 存储代码压缩前后的位置的对应关系,有了Source Map,出错的时候,出错工具直接显示的源代码,而不是转换后的代码,方便后期的调试
在开发环境下,webpack默认开启了Source Map功能,出错的时候,控制台显示的错误行的位置,定位具体的源代码,但是默认的有一定的问题,默认的是生产后的代码的位置,行数不一致
在webpack-config.js文件中加入配置,就是和model平级
module.exports ={
mode: 'development',//mode值有development和production,开发阶段就用dev
devtool: 'eval-source-map', -----------------------------------------增加的配置
entry: path.join(__dirname,'./src/index.js'),
output: {
path: path.join(__dirname,'./dist'),
filename: 'js/boundle.js'
},
plugins: [htmlPlugin,cleanPlugin], //通过plugin结点,让HtmlPlugin生效
devServer:{
open: true, //代表是否自动打开
host: '127.0.0.1',
port:8090 //这样就不会出现占用
},
module:{ //所有第三方文件模块的匹配规则标识
rules
配置之后,行数就是前后一致的
在生产环境下,省略devtool选项,最终生成的文件不包含Source Map,这就可以防止原始代码通过Source Map泄露,这样定位的错位都是转化的混淆的代码,对调试不好可以设置为: 只显示行数,不暴露代码
devtool: 'nosources-source-map'
这样既方便调试,也不会泄露代码
但是devtool : ‘source-map’ 就是可以定位到源码,和之前的开发环境的eval-source-map的效果相同,十分不安全,一般就用nosources或者直接关闭