前端工程化
- 模块化(js模块h化,css模块化,资源的模块化)
- 组件化(复用现有的ui,结构样式,行为)
- 规范化(目录结构的划分,编码规范化,接口规范化 文档规范化,Git分支管理)
- 自动化(自动化构建,自动部署自动化测试)
webpack是什么?
webpack是一个现代JavaScript应用程序的静态模块打包工具 。当webpack处理应用程序时,它会在内部构建一个依赖图(dependency [/d??pend?nsi/] graph [/ɡr?f/]),此依赖图会映射项目所需的每个模块,并生成一个或多个bundle包!webpack本身是基于node.js开发的!
为啥要使用webpack
模块化开发
模块化开发是什么?
模块化开发其实就是封装细节,提供使用接口,彼此之间互不影响,每个模块都是实现某一特定的功能,同时也需要避免全局变量的污染。一个模块就是一个实现特定功能的文件,有了模块我们就可以更方便的使用别人的代码,要用什么功能就加载什么模块。
模块化编程进化历史
- 单例设计模式
- AMD「require.js」
- CommonJS
- CMD「sea.js」
- ES6Module
1.单例设计模式
最早的模块化,但是对引入文件的顺序要求严格:比如B.js依赖于A.js,则必须先引入A.js再引入B.js
- 缺点:虽然解决了模块化开发,防止了全局变量污染,但是依赖关系把数据处理变得`异常恶心
let xxxModule = (function () {
let time = new Date();
const query = function query() {
// ...
};
const handle = function handle() {
// ...
};
// 把供其它板块调用的方法,暴露到全局对象上
//「局限:暴露的内容比较多,则还会引发全局变量冲突」
// window.query = query;
return {
query,
handle
};
})();
2.AMD[require.js]
AMD思想是在单例模式的基础上,实现了对依赖的管控。
管控模式缺点)依赖前置,在具体开发模块代码之前,先把需要的依赖处理好,再开始开发
? - 用法:使用define导出,使用require导入。
//使用之前需要导入require.min.js包
//A.js
//用define定义导出的对象,变量名是A,导入的时候用A
//如果这个模块不需要导入其他模块,可以直接写函数
let A=define(function(){
let sum=function sum(...parmas){
return parmas.reduce((res,item)=>res+item);
}
return {sum};
})
//B.js
//当需要导入其他模块时,
//第一个参数传想要导入的模块名数组
//第二个参数是当前模块的函数代码
let B=define(["A"],function(A){
const average=function average(...parmas){
parmas.sort((a,b)=>a-b).pop();
parmas.shift();
return A.sum(...parmas)/parmas.length;
};
return {average} ;
})
//main.js
//配置依赖模块的导入公共路径
require.config({
baseUrl: "./lib"
})
//require用于导入,
//第一个参数是要导入的模块,不分先后顺序
//第二个参数是本身的函数
require(["A", "B"], function (A, B) {
console.log(A.sum(1, 23, 2, 4, 53, 8));
console.log(B.average(1, 23, 2, 4, 53, 8));
})
//最后只需要在html文件中带入main文件即可
3.Common.js:应用在服务器端[node]的模块化
缺点?不能在服务器端使用
应用`node/webpack`支持CommonJS规范
用法:导出用`module.exports`,导入模块用`require`
导出:可以导出函数或对象
// 直接导出一个函数
module.exports= function sum(...params){
return params.reduce((result,item,index)=>{
return result+item;
})
}
//导出一个对象
module.exports={
sum
}
- 导入:导入目标路径必须以./开头,文件后缀可以省略
//如果目标模块导出的是一个函数,则可以直接导入那个函数
let suma=require("./B.js");//xxx.js后缀可以省略
//如果目标模块导出的是一个对象,则可以直接导入这个对象
let B=require("./B");//xxx.js后缀可以省略
let {suma,sumb}=require("./B");//可以对B直接解构赋值
4. ==CMD[sea.js]==:应用在浏览器端的模块化,旨在让Common.js能够在浏览器运行,但后期被webpack干掉了
5.- ==ES6Module==:ES6新增的模块化,主要应用在浏览器端的模块化
- ? ?优点:我们以后在浏览器端模块化开发时,都会用ES6Module开发
- ? ? 注意点:我们在html文件中导入模块时必须声明==**`type="module"`**:==`<script type="module" src="./A.js"></script>`
- ? ?用法:用export/export default导出模块,用import导入模块,一个模块可以导入无数次
`export default导出,import导入`:export default只能用一次
//A.js
let numa="111";
//export default 写法
// export default:导出模块中的一个默认值:(可以是任何类型数据)每个模块只能用一次
//模块导出的实际是一个Module对象,我们export default的值,会存在Module对象中的default属性
export default numa;
//B.js
//导入时文件路径必须由./开头,且不能省略文件后缀.js
//导入A模块export default的值
//导入A.js中export default导出的默认值,且把值赋给num
//我们这样的写法,A模块导出的Module对象,会把它的default属性值赋值给num
import num from "./A.js";
//因为我们接收的不是module对象而是default的值,所以不能直接解构赋值,需要再通过num结构赋值
let {na,nb}=num;
`export 导出,import导入`:export能用无数次
//A.js
let numb="222";
let numc="333";
let obj1={name:"lisa",age:18}
//export 写法
//方式一:导出一个变量,需要在export后定义赋值,不能在外面定义再只传变量名
export let nume="444";
//方式二:通过花括号的,可以同时导出多个已经定义的变量
//这种导出方式实质上是给Module对象赋值,将numb/numc/obj1赋值给Module,变为Module的私有对象
export {numb,numc,obj1};
//B.js
//将A模块导出的整个Module对象导入,拿到对象后就能通过A.xxx调用其内部的属性了
import * as A from "./A.js";
console.log(A.numb);//"222"
//因为导入的是一个Module对象,所以我们可以对这个对象进行解构赋值,获取其中的属性,因为default是一个内置私有属性,所以不能导出defalut
import { numb,numc,obj1 } from "./A.js";
console.log(numb);//"222"
?webpack使用
webpack主要构成是什么
- webpack是一个基于nodejs实现的平台
webpack即支持ES6Module,又支持CommonJS规范,而且两个规范可以混用
- 入口:从哪个文件开始打包编译
- ?出口:打包的文件放到哪里
- mode?存在生产环境(production)和开发环境(development)
- loader:加载器基于各种加载器实现代码的编译
- plugins插件 基于插件实现打包压缩(也包含部分文件的编译)
- 优化项...
wepack如何使用?
全局?执行 webpack命令按照我们配置好的文件,实现文件的编译打包
本地?在`package.json`中的`"scripts"`属性下配置`"xxx",然后在命令窗口中执行`npm run xxx
我们需要把配置写在项目的`webpack.config.js`文件中,可配置entry/output/mode/loader/plugins/优化项.
- - ==第一步==:删除全局的webpack:`npm uninstall webpack webpack-cli -g`
- - ==第二步==:安装局部的webpack:`npm i webpack webpack-cli --save-dev`
- - ==第三步==:检查项目目录中是否存在src路径,src中是否存在index.js目录
- - ==第四步==:在`package.json`中的`"scripts"`属性下配置`"xxx":"webpack"`,"xxx"是自定义名,然后在命令窗口中执行`npm run xxx`来实现打包【使用默认打包方式:默认打包index.js ,默认生成在:dist/main.js】
- - ==第五步==:配置`webpack.config.js`,实现自定义的控制webpack打包配置
webpack的安装方式
方式一:将webpack安装到全局
- ?`$ npm i webpack webpack-cli -g ?/ npm i webpack@5.49.0 webpack-cli@4.7.2 -g`
- xxx-cli 这样的模块就是命令模块,目的是使用xxx命令
- ==优点==:所有的项目不用再单独安装webpack包,都能在文件夹下的命令行窗口使用`webpack`命令执行打包
- ? - ==缺点==:所有项目使用的webpack版本一样,容易引起版本冲突
*方式二:想给哪个文件夹打包,就给哪个文件夹安装webpack
- ?npm i webpack webpack-cli// npm i webpack@5.49.0 webpack-cli@4.7.2 安装在本地的开发依赖中,是把打包编译后的结果放到生产环境中
- - ==优点==:每个项目都有自己的webpack版本号,互不影响,也是我们做项目的时候真正用的安装方式
- ? - ==缺点==:不能在自己的命令行窗口,用`webpack`命令执行打包,如果想用,有两种方
- 如果wepack版本在5.2以上:使用`npx webpack`不用配置命令就可以打包式:
- 让安装在本地的模块可以基于命令操作,
- 也可以在`package.json`中的`"scripts"`属性下配置`"xxx":"webpack"`,"xxx"是自定义名,然后在命令窗口中执行`npm run xxx`来实现打包
// 为防止全局安装webpack导致版本冲突,真实项目中以本地安装为主
$ npm init -y
$ npm install webpack webpack-cli --save-dev
OR
$ yarn add webpack webpack-cli -D
webpack的默认打包
?webpack有默认的打包路径:我们不需要配置`webpack.config.js`,当我们执行webpack命令的时候就可以打包
- ==默认入口是==:`文件夹/src/index.js`
- ==默认出口是==:`文件夹/dist/main.js`( 按照入口中的代码和模块依赖,最后就行打包,如果没有,会自动生成)
* 默认会打包SRC目录中的JS文件(入口默认index.js)
* 打包完成的目录默认是DIST/MAIN.JS
* webpack默认支持CommonJS和ES6 Module的模块规范,依此进行依赖打包
*/
$ npx webpack
执行webpack打包命令触发的查找机制
? ? 本地项目中可以使用的命令,都按照这个文件开始执行打包编译
- ==机制==:当我们运行 npm run serve,会把webpack命令执行
- ? - @1 首先]到项目的node_modules中的.bin的目录中查找
- ? - @2找到webpack这个文件,按照这个文件开始执行打包编译
自己配置webpack打包规则
1.项目根目录中新建 webpack config.js
- 当我们执行webpack命令的时候,首先看根目录是否有有这个文件,有这个文件按照这个文件中提供的规则进行打包编译
- 如果没有这个文件按照默认的规则进行打包编译
let path = require('path');
//基于CommonJS规范导出配置项(webpack基于node开发的)
module.exports = {
//编译模式 开发环境development 生产环境production
mode: "development",
//编译入口
entry: './src/main.js',
//编译出口
output: {
//输出的文件名
filename: 'bundle.js',
//输出目录 需要是一个绝对路径
path: path.resolve(__dirname, 'dist')//获取当前目录的绝对路径
}
}
|