1、模块化
前端模块化
早期: stage1: 按文件划分;这是最原始的模块划分方式,把原来写在一个js文件里面的逻辑拆分出来,写在不通的js文件中,通过script标签去引用;这样所有的文件都在全局中工作,没有自己私有的作用域,容易导致变量污染;一旦模块太多,很可能造成明明冲突;而且对于每个模块的以来关系不好管理;
stage2: 按命名空间划分;在stage1的基础上,把所有的方法写在一个对象里,然后引用文件后就可以使用[对象名][方法名]去调用,这样可以减小命名空间的机率,但是没有私有空间,也可以在全局引用;
stage3: IIFE(立即执行函数);将函数放在一个立即执行函数中,然后再向全局挂载一个对象,对象里面是我需要暴露的方法;这样就让每个模块有了私有成员,只能通过内部闭包使用;
缺点:我们都是通过script标签引用,就会造成引用的复杂;
模块化标准:
commonJs: 这是一个再node上应用的标准,他是同步加载模块,在node启动的时候回去加载我们所需要的模块;但是在浏览器端如果使用commonJs,在每次页面刷新的时候就会导致同步去加载大量的资源,使得浏览器运行效率低下;
AMD:这是为浏览器应运而生的模块化规范;它有别于commonJs的同步加载,它是一个异步加载的;同时还产生了一个require.js库,AMD提供了define去定义模块,他可以为当前的模块提供一个私有空间;同时提供require去引用定义的模块,当我们去使用这个require方法加载模块的时候,它的内部会自动生成一个script标签去加载我们所要执行的方法;使用复杂;如果模块划分太细,会导致js频繁加载,也会使浏览器运行效率低下;
ESModule:这是ES6定义的最新的模块标准;通过在script标签设置type=module可以使用; 特性: 它默认使用严格模式; 每一个script都是一个模块,他们各自拥有私有作用域; ESM是通过CORS的方式去请求外部js模块; ESM的script标签会延迟执行脚本; --相当于defer
ESM导入导出注意事项: export default { name , age } === 导出一个对象 它可以在页面上直接使用import obj from './xxx'引入 使用方法: obj.name= "李四" 注意事项:我们导出的时候看似是一个基础类型,但是实际上他只是把引用地址暴露出去,我们在页面上使用的时候其实是用暴露出来的引用地址。
export { name ,age } !== 导出一个对象 它需要使用import { name,age } from './xxx'单个引入我们导出的变量 直接使用: console.log(name);我们可以直接使用,但是我们不能去修改,如果我们去修改这个name值,name = "张三",浏览器会报错,告诉我们不能去修改一个常量;
import动态引入模块:
import('./xxx.js').then(res => {
console.log(res)
})
由于ESM是一部加载模块的机制,所以它可以返回一个promise函数,使用.then的链式方法可以去获取到引入的模块。
2、什么是webpack?
webpack可以看作一个模块打包机:它会分析你的项目结构,找到js模块以及其他浏览器不能直接运行的拓展语言(sass,ts),并将其经过打包以供浏览器使用。
3、为什么要使用webpack?
gulp和grunt是基于任务和流工作的。有点像jq。我所理解的解释找到一个文件,然后对它一顿操作猛如虎,然后去更新流上的数据,这样的一条管道(pipe)就构成了一个任务,多个任务就构成了整个web的构建流程;(这个就像jq,我们找到一个节点,然后对他进行各种链式的操作,最后把数据和样式给安排的明明白白的,再来一个点击事件,这个节点的操作就算做完了,然后所有的dom操作都做完了,那么这个项目也就安排明白了。)
webpack是基于入口(entry),他会自动递归解析入口文件所需要加载的所有资源,形成一棵树型结构,然后顺着这棵树去加载解析。webpack只认识js,但是我们的项目中会有很多非js的语言,比如css,sass等等,这时候可以使用对应的loader对这些资源进行转换,然后再让webpack去打包。
4、webpack基本配置
1、entry:告诉webpack从哪个入口进入,然后从入口文件开始逐步去查找依赖,构成一张树形依赖图
2、output:告诉webpack打包之后的文件输出到哪里,可以置顶输出文件的地址以及文件名。
3、loader:webpack只认识javascript语言,但是我们在编写代码的时候其实还有css、sass、以及图片等其他的资源文件,这时候如果我们直接用webpack内置模块去打包,那么浏览器就是报警高,警告我们需要使用其他的loader去解析那些它不认识的文件类型;这时候我们就需要使用loader。
loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。
对于不通的资源文件我们需要使用不同的loader进行解析转换。
4、plugin:我们使用loader是为了让webpack能够解析其他资源便于顺利打包。如果我们需要为我们的程序带来更多的自动化配置,我们就需要使用plugin插件去扩展程序应用。打个比方,就比如造车,loader负责准备材料组装好车辆,但是更多的自动化操作,比如自动泊车这些功能就需要plugin插件带来。
5、webpack配置步骤
1、新建项目webpack_demo,npm init命令初始化package.json文件;
2、项目安装webpack webpack-cli 插件,为webpack_demo项目提供相应的安装包。
命令完成后,我们会发现项目中多了一个 node_modules 文件夹,该文件夹是用来存放项目中安装的依赖包。
3、在根目录下新建一个src和dist文件夹、index.html、webpack.js文件,在src文件夹下面新建一个index.js和module.js文件。
4、配置webpack.js文件夹的内容,引入path模块,配置入口(entry)为src路径中的index.js,出口(output)为根目录下的dist文件夹(path.resolve(__dirname,'diet'));
5、根据自己的需求配置loader和plugin;
6、webpack打包运行的工作原理
1、打包后的js文件其实是一个IIFE(自执行函数),引入的模块作为数组传入,这样保证了每个模块的私有性;
2、运行html文件,打开sources,找到main.js,进行单步调试;
3、installedModules = {},用来缓存已加载的模块;
4、webpack_require()用来加载模块的函数;
4.1、首先回去判断是否已加载,如果已加载就从installedModules里面取,如果没有就往installedModules里面添加一个;
4.2、然后往模块上面添加module.exports,module等导出成员对象还有require函数,这样就可以使用导出和import导入;
4.3、.r函数其实就是给我们的模块打上一个标记,告诉我们的模块是一个esModule;
4.4、这个时候再去调用__webpack_require__(),加载后面的模块,重复上面的步骤,最后通过默认的default方法导出;
4.5、拿到导出的模块,然后再去进行导出模块里面的操作;
7、基于webpack4构建vue的打包配置
1、在命令行中敲入如下命令:
mkdir Webpack-Vue && cd Webpack-Vue && npm init -y
然后你就可以在你的当前路径下看到一个叫 Webpack-Vue 的文件夹,里面有一个包含默认信息的 package.json 文件,打开并修改这个文件的一些内容。
然后我们在项目文件夹中创建以下几个文件夹:
- dist
- src、src/components
- build
mkdir src src/components dist build -p
在 src 下,创建入口文件 index.js。
touch ./src/index.js
在根目录下新建index.html
<!DOCTYPE html>
<html>
<head>
<title>Webpack Vue Demo</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="app"></div>
</body>
</html>
2、完成上述文件准备工作,我们需要安装本次我们需要使用的webpack模块
npm i webpack webpack-cli -D
然后你就可以看到你的项目文件夹中多了一个node_modules 文件夹,然后 package.json 文件中多了一个 devDependencies 属性(-D),里面包含了安装的依赖名称和依赖版本,现在暂时还只有 webpack 和 webpack-cli。
3、webpack配置准备
在build文件中新建build.js、webpack.prod.conf.js、webpack.dev.conf.js、webpack.base.conf.js 。
touch build.js webpack.prod.conf.js webpack.dev.conf.js webpack.base.conf.js
- webpack.base.conf.js 是最基础的打包配置,是开发环境和生产环境都要用到的配置
- webpack.dev.conf.js 就是在开发环境要使用的配置。
-
webpack.prod.conf.js就是在生产环境要使用的配置了。 - build.js 是通过 Node 接口进行打包的脚本。
做好准备工作,我们就可以开始对webpack进行配置了;
4、配置webpack
webpack-base-conf.js,公共配置:
const path = require('path');
const Html = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'bound.js'
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../index.html') //以根目录下的index.html为模版
})
]
}
//需要安装html-webpack-plugin
webpack-prod-conf.js,生产配置:
const path = require('path');
const merge = require('webpack-merge')
const base = require('./webpack.base.conf')
const { cleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = merge(base,{
mode: 'production',
devtool: 'source-map',
module: {
},
plugins: [
new CleanWebpackPlugin()
]
})
webpack-dev-conf.js,开发配置:
const path = require('path');
const merge = require('webpack-merge')
const base = require('./webpack.base.conf')
module.exports = merge(base,{
mode: 'development',
devtool: 'source-map',
devServer: {
contentBase: path.resolve(__dirname, '../dist'),//告诉本地服务从哪里提供内容且只有在您想要提供静态文件时才需要这样做
open: true //默认打开
}
})
现在我们已经做了一个最基本的webpack配置,检查一下是否能正常打包运行。
在index.js中添加
console.log('元神')
在package.json中修改script:
"build": "webpack --config build/webpack.prod.conf.js",
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"
命令行执行
npm run build
打开index.html文件,控制台输出“元神”,证明打包运行成功;此时一个最基础的webpack打包配置就做好了。但是这个配置并不能够支持vue解析打包,还需要对vue进行一些特殊的操作,这时候打开vue-laoder官网,就很有必要了...
5、首先我们需要下载能够解析.vue文件的loader --vue-loader
npm i vue-loader -D
根据vue-loader中文网的介绍,在使用vue-loader的同时,我们还需要安装vue-template-compiler;
npm i vue-template-compiler -D
百思不得其解,这个vue-template-compiler是什么?为什么非要使用它?
vue-template-compiler模块将vue2模版预编译为渲染函数(template => ast => render),以避免运行时编译开销;
它是从vue源码中抽离出来的,它和compile的函数是一致的,只是vue-template-compiler开放的参数和方法更多,因此vue-template-compiler必须和vue的版本保持一致(同一份源码);
同时我们再准备一个vue-loader-plugin插件,它的职责是将你定义过的其它规则复制并应用到 .vue 文件里相应语言的块。例如,如果你有一条匹配 /\.js$/ 的规则,那么它会应用到 .vue 文件里的 <script> 块。
npm i vue-loader-plugin -D
这时候做一点babel的配置,为了让es6的代码转换成es5,虽然大部分浏览器都认识es6,但是还是考虑一下少部分;
首先安装 babel-loader、babel-preset-env 和 babel-core。需要注意的是,如果你的 babel-loader 是 7.x 版本的话,你的 babel-core 必须是 6.x 版本;如果你的 babel-loader 是 8.x 版本的话,你的 babel-core 必须是 7.x 版本。如果不这样的话,Webpack 会报错。
npm i babel-loader@7 babel-core babel-preset-env -D
我们安装vue-style-loader,css-loader ,style-loader,并进行配置
npm i css-loader vue-style-loader style-loader -D
在web pack.prod.conf.js中使用vue-loader:
module:{
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_module/ //让babel不去处理node_module里面的js文件
},
{
test: /\.css$/,
use: ['style-loader','vue-style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../index.html') //以根目录下的index.html为模版
}),
new VueLoaderPlugin()
],
resolve:{
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': path.resolve(__dirname, '../src'),
},
extensions: ['*','.js','.vue','.json'] //配置后在引入文件的时候可以不写后缀
}
再在根目录下面新建.babelrc文件
{
"presets": [
["@babel/preset-env",
{
"useBuiltIns": "usage",
"targets": {
"browsers": ["last 2 versions", "ie >= 10"]
}
}
]
]
}
babel-preset-env 帮助我们配置 babel。我们只需要告诉它我们要兼容的情况(目标运行环境),它就会自动把代码转换为兼容对应环境的代码。
接下来我们可以对文件和一些引入资源进行打包配置:
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
经过一些列的操作,我们就配置了一个关于vue的webpack,现在让我们测试一下,首先安装vue
npm i vue -D
我们改写index.js
import Vue from 'vue';
import App from './App';
new Vue({
el: '#app',
template: '<App/>',
components: { App }
});
新建一个App.vue
<template>
<h1>Hello World!</h1>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
html, body {
padding: 0;
margin: 0;
box-sizing: border-box;
font-size: 16px;
color: red;
}
</style>
命令行执行
npm run dev
看到一个大大的红色一级标题 —— Hello World!这时候我们的webpack配置就生效了