重点中的重点,懂得都懂
第一章 模块化开发介绍
1.1 为什么需要模块化开发
1.1.1 JavaScript的原始功能
- 在也买你开发的早期,js制作作为一种脚本语言,做一些简单的表单验证或者动画的实现等,那个时候代码还是很少的
- 但是随着ajax的异步请求的出现,慢慢形成了前后端的分离
- 客户端需要完成的事情越来越多,代码量与日俱增
- 为了应对代码量的剧增,我们通常会将代码阻止在多个js文件中,进行维护
- 但是这种维护方式,会出现很大的问题
- 例如:全局变量同名的问题
- 另外,这种代码的编写方式对js文件的依赖顺序几乎是强制性的
对于全局变量同名的早期解决方案: 闭包(匿名函数)
-
将我们一个文件中的代码都放在一个匿名函数里面,形成一个闭包,这样,不同文件之间的变量就不会重名了
-
但是,这样会导致另外一个问题, 代码的复用性变得十分低,我想在另外一个文件引用之前的变量,抱歉,做不到
-
这时候,模块化的雏形就有了,我们可以在闭包中创建一个obj对象,将我们想要传递的东西都放进去,然后return obj,同时,我们不能再用匿名函数了,给我们的闭包函数定义一个名字,这样别的文件通过调用这个函数就可以接收到这个obj,同时还可以使用我们想要的变量或者方法了
-
//给闭包函数命名 var module = (function (){ var obj = { }; var flag = true; function sum(num1,num2) { return sum1 + sum2; } obj.flag = flag; obj.sum = sum; return obj; })() //这是另一个闭包函数 (function(){ //我想要调用flag var flag = module.flag; })()
-
这就是模块的最基础的封装事实上的模块的封装还有很多高级的话题
- 幸运的是,前端模块化开发已经有了很多既有的规范,以及对应的实现方案
-
常见的模块化规范
- CommonJS 😦 Node用他的规范实现了)
- AMD
- CMD
- 也有ES6的Modules
1.1.2 CommonJS规范(了解)
模块化有两个核心: 导入和导出
在Node环境中,就可以使用这种语法
-
CommonJS的导出
-
moudle.exports = { flag: true, test(a,b){ return a+b; }, demo(a,b){ return a+b; } }
-
-
CommonJS的导入
-
//CommonJS的模块,这里相当于对对象的解构 let { test , demo , flag} = require('mouduleA'); //等同于 let a = require('mouduleA'); let test = a.test; let demo = a.demo; let flag = a.flag;
-
1.2 ES6的module (常用)
1.2.1 export和import的使用
//index文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>我就是来搞笑的</h2>
<script src="bbb.js" type="module"></script>
<script src="aaa.js" type="module"></script>
</body>
</html>
注意我在script标签加入了
type="module"
这段代码,意思就是将这对应的文件当作一个模块使用
这个js文件用来演示导出
//aaa.js
let flag = true;
let name = '小明';
let age = 20;
function sum(a, b) {
return a + b;
}
if (flag) {
console.log(sum(20,30));
}
//现在我想要用ES6的export
//导出方式1
export {
flag, sum }
//导出方式2
export var num1 = 1000;
export function shout(){
console.log("我是你爹!");
}
export class person {
aa;
run() {
console.log('我在跑步')
}
}
这个js文件用来演示导入
//bbb.js
//这个文件里面我想使用aaa.js里面的flag
import {
flag , sum} from './aaa.js';
if (flag){
console.log("小明真是个天才 , 他知道 1+1 =" + sum(1,1));
}
import {
person } from './aaa.js';
let aaa = new person();
aaa.run();//我在跑步
aaa.aa = '你爹';
console.log(aaa.aa);//你爹
还有可以导出所有的语法import * from 'xxx.js'
1.2.2 export default的使用
有时候,我们想要将引入的东西自定义命名,那么我们就用到了export default
//导出的文件代码
const address = '北京市';
const number = 123124;
//这个使用的次数只能是一次,可以导出变量,对象,函数等
export default address;
//导入的文件代码
import addr from './exportdefault.js';
console.log(addr);//这里的addr就是导出的address,名字是我们自定义的
//输出结果就是北京市
第二章 Webpack
2.1 Webpack介绍
本质上来讲,他是一个现代的JS应用的静态模块打包工具
我们从两点来解释上面那句话: 模块和打包
前端模块化
- 在ES6之前,我们想要进行模块化开发,就必须借助于其他的工具,让我们可以进行模块化开发
- 并且在通过模块化开发完成了项目后,还需要处理模块间的各种依赖,并且将其进行整合打包
- 而webpack其中一个核心就是让我们可能进行模块化开发 (为我们使用模块化的一些方案(AMD,CMD,CommonJS等)提供底层支撑,打包的时候会自动转化为浏览器可以解析的样式) ,并且会帮助我们处理模块间的依赖关系
- 而且不仅仅是JS文件, 我们的CSS,图片,json文件等等再webpack中都可以被当作模块来使用 (在后续我们会看到)
打包
- 简而言之就是将webpack中的各种资源进行处理,(比如压缩图片,将scss文件转化为css,将ES6语法转化为ES5语法,将TS转化为JS等等操作 ),合并成一个或者多个包(Bundle)
webpack和 grunt/gulp的对比
- grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心.
- webpack更加强调模块化开发管理,而文件压缩合并,预处理功能,是他的附带功能
2.2 webpack安装
-
安装webpack首先需要安装Node.js, Node.js 自带了软件包管理工具(npm [node passage manager])
-
全局安装
npm install webpack -g
-
局部安装: –save-dev是开发时的依赖,项目打包之后不需要继续使用
npm install webpack --save-dev
-
为什么安装全局之后,还需要局部安装呢?
- 在终端直接执行webpack命令,使用的是全局安装的webpack
- 当在
package.json
中定义scripts时, 其中包含了webpack命令,那么使用的是局部webpack
这里我们安装的是webpack3.6.0,因为它对应的cli2脚手架更容易让我们看见cli2对webpack的配置
2.3 webpack的基本使用
文件结构
打包命令 : webpack ./src/main.js ./dist/bundle.js
(这句话的意思就是将src/main.js
打包, 打包在dist
目录下,生成bundle.js
文件)
注意: 我们一般只用打包一个main.js就行了,因为webpack会自动将main导入的所有依赖,他所引入的依赖的依赖,…等等都一起打包了,相当于main.js就是一个入口,webpack实际上打包了所有有联系的文件
//main.js
import aaa from './mathUtils.js';
console.log(aaa.add(20,30));
console.log(aaa.mul(20,30));
//mathUtils.js
export default {
add(a, b) {
return a + b;
},
mul(a, b) {
return a * b;
}
}
我们可以发现,上面的打包命令没有包含
MathUtils.js
,但是却成功一起打包了
我们只需要在index.html 下引用 我们上面生成的bundle.js文件就可以了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="dist/bundle.js"></script>
</body>
</html>
//输出的结果就是50,600
2.4 webpack的配置
刚才我觉得我的打包命令每次都要输入太麻烦了,我想要只输入webpack,就能够实现刚才的指令
这里,我们通过配置就能够实现
2.4.1 webpack.config.js配置
const path = require('path');
module.exports = {
entry: './src/main.js',
output: {
//这里要写绝对路径,我们这里最好获取动态的路径
//下面的这个__dirname是node的东西,它代表项目的根目录的绝对路径
path: path.resolve( __dirname,'dist'),
filename: 'bundle.js'
},
}
有了这个,我们只用在终端输入webpack就可以直接快捷打包了
2.4.2 package.json配置
里面配置了一些项目的信息,npm包管理的文件(暂时用不上,后面才会用上)
使用npm init
命令来建立package.json配置文件、
将webpack命令映射到npm run build
上面
那么,我们在这个文件中的script
里面加上"build": "webpack"
{
"name": "meetwebpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"author": "CJY",
"license": "ISC"
}
同理, 上面那句话的
"test": "echo \"Error: no test specified\" && exit 1"
意思就是我们在终端输入的 npm run test 等同于 在终端输入echo \"Error: no test specified\" && exit 1
一些问题
对于上面这段代码,我有想说的,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
}
//这里面的命令,其实优先调用的是局部的webpack的版本(即局部安装,运行时依赖的webpack版本),要是没有运行时依赖的webpack,就会调用我们全局安装的webpack的命令
在实际开发中,我们电脑中的webpack版本和我们做的项目的webpack需要的版本往往是不一样的,所以安装局部依赖很重要
注意,就算我们安装了局部依赖,但是在终端或者cmd中,如果我们直接输入webpack,那么还是会默认调用全局的webpack , 只有当我们输入npm run build
的时候,调用的是局部的
2.4.3 css文件的配置
Loader
- Loader是webpack中一个非常核心的概念
- webpack用来做什么?
- 在我们之前的实例中,我们主要是用webpack来处理我们写的js代码,并且webpack会自动处理js文件之间的依赖
- 但是,我们在开发中·不仅仅有基本的js代码的处理,我们也需要加载css,图片,也包括一些高级的将ES6转化成ES5代码,将TS转化为ES5代码,将SCSS,LESS转化为css,将.jsx , .vue 文件转化为.js文件等等
- 但是,webpack本身的实力是不支持这些转化的
- 这时我们就给webpack拓展对应的loader就可以了
- Loader的是用过程
- 步骤一: 通过npm安装需要是用的loader
- 步骤二: 在webpack.config.js中的modules关键字下面进行配置
- 大部分loader我们都可以在webpack官网中找到,并且学习对应的用法
css文件的配置
我们也要将css文件作为一个模块打包进js文件中
我们来看看样式有什么loader
实现步骤
-
安装
css-loader
和style-loader
-
前者作用:加载 CSS 文件并解析 import 的 CSS 文件,最终返回 CSS 代码
-
后者作用: 将模块导出的内容作为样式并添加到 DOM 中
-
//输入 npm install --save-dev css-loader npm install --save-dev style-loader
-
-
在
webpack.config.js
文件中添加下面的代码-
module: { rules: [ { test: /\.css$/i, //是用loader的时候,是从右向左读取的 use: ['style-loader', 'css-loader'], }, ], },
-
-
在
main.js
文件中添加依赖-
require('./css/xx.css');
-
-
运行打包代码,就ok了
注意: 如果你的css-loader
的版本太高,而webpack版本比较低,那么就会报错,报错内容为this.getResolve is not a function
2.5 webpack的文件处理
2.5.1 less文件的处理
除了css和js文件,我想要在项目中是用less,sass,来写样式,webpack是否可以帮助我们做到呢?
- 我们这里以less文件为例,其他都是一样的
步骤如下
-
创建一个less文件
-
在我们的main.js中引入
-
安装
less-loader
和style-loader
和less
-
在
webpack.config.js
文件的rules数组中添加下面的东西-
{ test: /\.less$/i, loader: [ // compiles Less to CSS "style-loader", "css-loader", "less-loader", ], },
-
-
运行即可
注意: 在js中,谁的css文件后引入,权重相同的情况下,谁的样式就会优先显示
2.5.2 图片文件的处理
我们看看文件有什么loader
现在1,3,4 都合并成为一个ref-loader
了
我想要在css中的bg里面引入背景图片
我们这里使用url-loader
它的作用 : A loader for webpack which transforms files into base64 URIs. (转化为base64编码)
-
安装
url-loader
和file-loader
-
在
webpack.config.js
中添加下列代码-
{ test: /\.(png|jpg|gif|jpeg)$/i, use: [ { loader: 'url-loader', options: { //当图片大小小于limit的时候,会将图片编码为base64格式,如果大于,那么就保留图片原先的格式,但是我们需要使用file-loader来加载这个图片 limit: 819200, }, }, ], },
注意: 当我们使用file-loader的时候,由于文件没有转化为base64,所以他是以文件的形式单独存在,所以,打包的时候,也不能打包进js文件里面,要单独打包,file-loader自动将打包的图片生成到了dist文件夹中,名字是hash生成的
-
我们能够在这个配置文件中的output中添加
publicPath: 'dist/'
,这样所有有关url的路径都会自动在最前面加上这个前缀
-
-
在css中添加图片的位置
-
body { background: pink url("../img/1.png") no-repeat; }
-
-
运行
在图片的大小小于limit的时候,我们看看图片的路径张什么样
我们会发现,图片不是路径,他就是一串编码
大于limit的时候,看看图片的路径
我们可以看到已经变成一个图片的路径了,图片被单独保留下来了
打包的图片的自命名和保留路径
图片默认名字是hash生成了名字,但是,在开发中,对打包的图片的名字和保留的路径往往有要求
options有name属性,name属性有下面的选项
- 所以,我们可以在options中添加如下选项
- img: 文件要打包到的文件夹
- name: 获取图片原来的名字,放在该位置
- hash8: 为了防止图片名称冲突,依然使用hash, 但是我们只保留8位
- ext: 使用图片原来的拓展名
代码示例
{
test: /\.(png|jpg|gif|jpeg)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
//我们引用的时候不用担心这里会引发找不到图片路径的问题
name: 'img/[name].[hash:8].[ext]'
},
},
],
},
这样,打包的图片就会自动生成在
idst/img
文件夹下,同时,文件的名字是图片原本的名字.hash前8位.原来的后缀名
2.6 语法转换
下面是webpack与语法转换相关的loader
我们这里就来讲讲babel -loader
: 它可以将所有ES5+的代码打包成为ES5的代码
步骤
-
安装
babel-loader
:npm install -D babel-loader @babel/core @babel/preset-env webpack
-
webpack.config.js
文件中添加下面的代码-
{ test: /\.m?js$/, //这句话的意思就是排除这两个文件夹 exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }
-
-
运行
2.7 使用vue的配置过程
项目开发中,我们使用的是vue文件,所以我们要配置
步骤
-
npm安装vue:
npm install vue --save
,我们这里安装的就是全局依赖,并不是开发时依赖 -
在
main.js
里面引入vue,同时在index.html
中添加挂载的根元素-
//main.js中引入vue import Vue from 'vue'; const app = new Vue({ el: '#app', data: { message: '我是你爹!' } })
-
-
如果运行的vue是
runtime-only
版本(这个版本不支持包含模板),我们要使用runtime-cpmpiler
版本(可以有template),那么我们就在webpack配置文件中添加下列代码-
resolve: { alias: { //vue指向一个具体的文件夹 'vue$': 'vue/dist/vue.esm.js' } } //注意:这个resolve是和entry,output等并列的
-
-
打包运行即可
2.8 Vue的终极使用过程
我们是用vue后缀的文件来疯转一个组件
有什么作用?
-
main.js
什么都不用写,只用负责引入vue组件就行了-
//main.js import Vue from 'vue' import App from './vue/App.vue' require('./css/special.less'); require('./css/common.css'); new Vue({ el: '#app', //这行代码至关重要 template: `<App/>`, data: { }, //这行代码也很重要 components: { App } });
-
-
引入
vue-loader
和vue-template-compiler
,并在webpack配置文件里面加上-
{ test: /\.vue$/, use: ['vue-loader'] }
-
-
在
index.html
中我们只用负责写一个div就行了-
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> </div> <script src="dist/bundle.js"></script> </body> </html>
-
-
我们创建有我们的vue文件
-
App.vue
是所有vue组件的父组件,可以认为是组件树的根节点-
//App.vue <template> <div> <h2>{ {message}}</h2> <cpn></cpn> </div> </template> <script> import cpn from './cpn.vue' export default { name: 'App', data() { return { message: '我是你爹!' } }, components: { cpn } } </script> <style scoped> </style>
-
-
可以看到
App.vue
有一个子组件,叫做cpn.vue
-
//cpn.vue <template> <div > <h2 id="aaa">我是{ { name }}!!!!!</h2> <p>就这?</p> </div> </template> <script> export default { name: "cpn", data() { return{ name: '我是CJY' } } } </script> <style scoped> #aaa { color: aqua; font-size: 40px; } </style>
-
-
来看看效果
可以看到,显示的就是App.vue模板里面的所有内容 (背景样式请忽略)
最后看看文件的结构
2.9 Plugin的使用
2.9.1 介绍
什么是plugin?
- plugin是插件,通常是用于对现有的架构进行拓展
plugin和loader的区别
- loader主要用于转化某些类型的模块,他是一个转换器
- plugin是插件,他是对webpack本身的拓展,是一个拓展器
怎么使用?
- npm先安装
webpack.config.js
里面配置插件
2.9.2 版权插件使用
该插件的i那个字叫做BannerPlugin
,属于webpack自带的插件
作用,每个文件最上面加上版权声明的注释
使用步骤:
-
因为自带,所以不用安装
-
修改
webpack.config.js
里面的配置-
//最上面先声明,顺便一提,这种导入方法就是默认在node_modules文件夹里面寻找 const webpack = require('webpack'); module.exports = { plugins: [ new webpack.BannerPlugin('最终版权归CJY所有') ] }
-
-
打包
效果如图
2.9.3 HtmlWebpackPlugin的使用
目前,我们的index.html是放在根目录下的
- 但是,真实发布项目的时候,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.jtml文件,那么打包的js等文件也就没有意义了
- 所以,我们要使用上面这个插件来达到这种效果
HtmlWebpackPlugin插件的作用
- 自动生成一个index.html文件
- 将打包的js文件,自动通过script标签插入到body中
使用步骤
-
安装
npm install html-webpack-plugin --save-dev
-
在webpack配置文件中的
plugin
中加上下面这句话-
//同时,最上面记得引入 const HtmlWebpackPlugin = require('html-webpack-plugin'); //这里是plugin数组里面添加的内容 new htmlWebpackPlugin({ template: 'index.html' })
注意: 这里的template表示根据什么模板来生成index.html
另外,我们需要删除之前在output中的publicPath属性,否则插入的script标签中的src可能会有问题
-
-
打包,运行,我们会发现在dist文件夹下面有
index.html
文件,同时,图片引入的路径问题就也解决了
2.9.4 js压缩的插件
项目发布之前,我们要对js文件进行压缩处理
我们使用第三方插件uglifyjs-webpack-plugin
,并指定版本号为1.1.1,和cli2保持一致
使用步骤
-
安装
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
-
在webpack配置文件中添加
-
const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin'); new uglifyjsWebpackPlugin()
-
转载:https://blog.csdn.net/m0_47652477/article/details/116722685