5npm相关
npm i webpack --save-dev 开发环境下的依赖
运行cnpm run dev自动打开
//package.json文件
"scripts": {
"dev": "webpack-dev-server --open --port 3000 --hot"
}
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server",
"build": "webpack"
}
webpack功能
- 代码转换
- 文件优化
- 代码分割
- 模块合并
- 自动刷新
- 代码校验
- 自动发布
loader功能
loader让webpack能够去处理那些非js文件,(webpack自身只能理解js)。loader能让所有类型的文件转换为webpack能够处理的有效模块,然后就可利用webpack的打包能力对其进行处理。
loader的两大目标:
- test属性,用于标识出应该被对应的loader进行转换的某个文件。
- use属性,表示在转换时,应使用哪个loader。
webpack使用
- webpack默认只支持js文件
- node webpack
//安装本地的webpack
cnpm i webpack webpack-cli -D
//表示开发依赖
//在命令行上输入webpack即可将入口文件打包
//webpack可以进行0配置
手动配置webpack文件
webpack配置文件webpack.config.js
module.exports = {
mode: 'development',//模式默认两种,production development
entry: path.join(__dirname,'./src/main.js'),
output: {
path: path.resolve(__dirname,'dist'),//路径必须是一个绝对路径
filename: 'bundle.js',//打包后的文件名
}
}
production:开发的未压缩的文件
development:开发的压缩的文件
更改配置文件名字
在package.json文件中的script对象上
scripts:{
"builder": "webpack --config webpack.configmy.js"
}
//命令行上运行 cnpm run builder即可
webpack以本地形式打开文件
cnpm i webpack-dev-server -D
//webpack.config.js
module.exports = {
devServer:{ //开发服务器配置
port: 3000, //在3000端口打开
progress: true, //显示进度条
contentBase: './build', //以当前目录运行程序
compress: true //启动压缩
}
}
//package.json文件
scripts:{
"dev": "webpack-dev-server"
}
webpack解析html模块
cnpm i html-webpack-plugin -D
//webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
output: {
path: path.resove(__dirname,'./dist'),
filename: 'bundle.[hash].js', //每次输出的文件都不同
//filename: 'bundle.[hash:8].js', 只显示8位hash值
}
plugins: [ //数组 放着所有的webpack插件
new HtmlWebpackPlugin({
template: './src/index.html' //以index.html文件为模板
filename: 'index.html', //打包后的文件名
minify: {
removeAttributeQuotes:true, //压缩时,删除属性中的双引号
collapseWhiteSpace: true, //折叠空行,将html文件打包成一行
},
hash: true, //添加哈希戳
})
]
}
webpack解析css模块
cnpm i style-loader css-loader -D //处理css文件
cnpm i less less-loader -D //less-loader会调用less进行解析
cnpm i node-sass sass-loader -D //处理sass和scss文件
cnpm i stylus stylus-loader -D //处理stylus文件
//main.js文件导入css文件
require('./index.css');
//webpack.config.js
module.exports = {
module: { //模块
rules: [ //规则 css-loader 处理@import这种语法的,将多个css文件合并为一个css文件
//style-loader 他是把css插入到head的标签中
//loader特点:希望一个loader处理一个功能
//只用一个loader用字符串,多个loader用数组,当有参数时可写成对象方式
//loader顺序:默认从右向左执行
{
test: /\.css$/,
use: [
{
loader:'style-loader',
options: {
insertAt: 'top' //将样式插到head顶部
}
},
'css-loader'
]
},
{ test: /\.less$/, use: ['style-loader','css-loader','less-loader'] }
]
}
}
抽离文件的插件
cnpm i mini-css-extract-plugin -D
//专门用于抽离css样式的插件
//webpack.config.js
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),
new MiniCssExtractPlugin({
filenmae: 'main.css' //抽离出的名字叫main.css
})
],
module: {
rules:[
{
test:/\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader' //先解析成css样式,然后使用MiniCssExtractPlugin这个插件的加载器将样式分离成一个main.css文件
]
}
]
}
自动加前缀的插件
cnpm i postcss-loader autoprefixer
module: {
rules: [
{
test: /\.less$/,
use:[
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
//新建一个postcss.config.js文件
module.exports = {
plugins: [require('autoprefixer')]
}
webpack中处理js模块
- 将es6转为es5
cnpm i babel-loader @babel/core @babel/preset-env -D
// @babel/core是babel的核心模块,@babel/preset-env 将高级语法转为低级语法
cnpm i eslint eslint-loader -D //语法检查
cnpm i @babel/plugin-proposal-class-properties -D //像class类这些高级语法
cnpm i @babel/runtime @babel/plugin-transform-runtime -D @babel/runtime //像generator遍历器,在plugins中配置
//webpack.config.js
module.exports = {
module: {
noParse: /jquery/, //不去解析jquery中的依赖库
rules: [
{ test: /\.js$/,
use: {
{
loader:'babel-loader', //用该模块将es6转为es5
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-proposal-class-properties','@babel/plugin-transform-runtime']
},
{
loader: 'eslint-loader', //校验es语法规范
options: {
enforce: 'pre' //强制先执行,因为loader默认是从下往上执行
}
}
}
},
exclude: /node_modules/, //不去node_module中找
include: path.resolve(__dirname,'src') //去src文件夹下找
}
]
}
}
webpack第三方模块的使用
module.exports = {
resolve: { //解析第三方包 common
modules: [path.resolve('node_modules')], //解析时先到该目录下寻找
alias:{ //别名 vue vue.runtime
bootstrap: 'bootstrap/dist/css/bootstrap.css'
}
}
}
cnpm i jquery
cnpm i expose-loader
//main.js文件
import $ from 'jquery'; //此$不是window上的的$
window.$ //undefined
import $ from 'expose-loader?$!jquery';
//expose-loader 暴露 全局的loader 内联的loader
//pre 前面执行的loader normal 普通loader 内联loader 后置 postloader
将$暴露给window的另一种方法
cnpm i jquery
cnpm i expose-loader
//main.js文件
import $ from 'jquery';
//webpack.config.js文件
module: {
rules: [
{
test:require.resolve('jquery'),
use: 'expose-loader?$!jquery'
}
]
}
在每个模块中注入$对象
//webpack.config.js文件
const webpack = require('webpack')
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),
new Webpack.ProvidePlugin({ //在每个模块中注入$
$: 'jquery'
})
]
}
//main.js文件
import $ from 'jquery' //运行npm run build时会将jquery打包
//不像将jquery打包,只需在webpack.config.js文件中
module.exports = {
externals: {
jquery: '$'
}
}
webpack打包图片
- 在js中创建图片
cnpm i file-loader -D
//file-loader默认会在内部生成一张图片到dist文件夹下,并把生成的图片的名字返回回来
//main.js文件
import logo from './logo.png';
//把图片引入,返回结果是一个新的图片地址
let image = new Image();
image.src = logo; //就是一个普通的字符串
document.body.appendChildren(image)
//webpack.config.js文件
module.exports = {
module: {
rules: [
{ test: /\.(png|jpg|gif)$/, use: 'fle-loader' },
]
}
}
-
在css引入background(‘url’)
-
使用标签
cnpm i html-withimg-loader -D cnpm i url-loader -D //在html文件中引入image,但是路径不在打包目录中的路径中 module.exports = { module: { rules: [ {test: /\.html$/, use: 'html-withimg-loader'}, { test: /\.(png|jpg|gif)$/, //做一个限制,当图片小于多少k时用base64来转化 //否则用file-loader产生真实的图片 use: { loader: 'url-loader', options: { limit: 200*1024, outputPath: '/img/' //输出路径放置在img目录下 publicPath: 'http://www.chenchuyin.com' //统一给图片加上该域名 } } } ] } }
webpack有多入口文件
cnpm i html-webpack-plugin - D
cnpm i @babel/core @babel/preset-dev @babel-loadedr @webpack-dev-server -D
//webpack.config.js文件
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
//源码映射 会单独生成一个sourcemap文件,出错了胡hi标识当前出错的列和行
devtool: 'source-map', //增加映射文件 可以帮我们调试源代码
//多入口
entry: {
home: './src/index.js',
other: './src/other.js'
},
output: {
//[name]表示 home 和 other [hash]添加哈希戳
filename: '[name].[hash].js',
path: path.resolve(__dirname,'dist')
},
plugins: [
//若无chunks,会把两个js文件都引入到index.html和other.html文件中
new HtmlWebpackPlugin({
tempalte: './index.html',
filename: 'index.html',
chunks: ['index'] //index.html文件引入index.js文件
}),
new HtmlWebpackPlugin({
tempalte: './other.html',
filename: 'other.html',
chunks: ['other','index'] //other.html文件引入other.js和index.js文件
})
]
}
-
不会产生单独文件,但是可以显示行和列
devtol:'eval-source-map'
-
不会产生列,但是是一个单独的映射文件
devtoole: 'cheap-module-source-map' //产生后你可以保存起来
-
不会产生文件,集成在打包后的文件中,不会产生列
devtoole: 'cheap-module-eval-source-map'
自动打包出实体文件
module.exports = {
watch: true, //监听文件,只要文件有所修改便直接生成新的实体文件
watchOptions:{ //监控选项
poll: 1000, //每秒 查看文件是否更改 1000次
aggreatement: 500, //防抖 如果一直输入代码,则每过500次再生成实体文件
ignored: /node_modules/ //不需要进行监控的文件
}
}
webpack小插件
- cleanWebpackPlugin
- copyWebpackPlugin
- bannerPlugin(内置的,不需要安装该模块)
cnpm i clean-webpack-plugin copy-webpack-plugin webpack -D
const CleanWebpackPlugin = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')
plugins: [
new CleanWebpackPlugin('./dist'), //打包之前先将dist目录清除
new CopyWebpackPlugin(
{from: 'doc', to: './'} //将doc.txt文件复制到输出的根目录下
),
new webpack.BannerPlugin('make 2019 by chenchuyin')
//会将这句话插入到出口js文件的开头部分
]
webpack实现跨域功能
//webpack-dev-server默认打开localhost:8080,若想访问3000端口,可使用代理
module.exports = {
devServer:{
proxy: { //重写的方式 把请求代理到express服务器上
// '/api': 'http://localhost:3000', //配置了一个代理
'api': {
target: 'http://localhost:3000',
pathRewrute: {'api': ''}
}
}
}
}
//server.js文件
const express = require('express')
let app = express();
app.get('/user', (req,res) => {
res.json({name:'陈楚吟'})
})
app.listen(3000);
//index.js文件
let xhr = new XMLHttpRequest();
xhr.open('GET','/api/user',true); //请求还是有api目录,显示时将其删除
xhr.onload = function(){
console.log(xhr.response);
}
xhr.send();
devServer:{
//我们前端只想单纯模拟数据
before(app){
app.get('/user', (req,res) => {
res.json({name: '陈楚吟'})
})
}
}
//server.js文件
const express = require('express')
let app = express();
app.listen(3000);
//index.js文件
let xhr = new XMLHttpRequest();
xhr.open('GET','/api/user',true); //请求还是有api目录,显示时将其删除
xhr.onload = function(){
console.log(xhr.response);
}
xhr.send();
cnpm i webpack-dev-middleware -D
devServer:{
//有服务端,不用代理来处理,在服务端中启动webpack端口用服务端端口(即前端页面和服务器在同一个端口)
}
//server.js文件
const express = require('express');
const webpack = require('webpack');
let app = express();
//中间件
let middle = require('webpack-dev-middleware');
let config = require('./webpack.config.js');
let compiler = webpack(config);
app.use(middle(compiler));
app.get('/user', (req,res) => {
res.json({name:'陈楚吟'})
})
app.listen(3000);
实现多线程打包
cnpm i happypack
//模块happypack可以实现多线程来打包进程
let Happypack = require('happypack')
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
include: 'src',
use: 'Happypack/loader?id=js'
},
{
test: /\.css$/,
use: 'Happypack/loader?id=css'
}
]
},
palugins: [
new Happypack({
id: 'css',
use: ['style-loader','css-loader']
})
new Happypack({
id: 'js',
use: [{
loader:'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react'
]
}
}]
})
]
webpack自带优化
//import 在生成环境下会自动去除没用的代码
//因tree-shaking方法会将没用到的代码自动删除掉·
//require(es6)模块会把结果都放到default上
module.exports = {
optimization: { //将公共部分抽离出来
splitChunks: { //分割代码块
cacheGrounps:{ //缓存组
common:{ //公共的模块
chunks: 'initial', //代码在刚开始时就被抽离出
minSize: 0, //代码有多少字节才被抽离
minChunks: 2 //代码块被使用多少次才被抽离
},
vendor:{
priority: 1, //抽离权重,较大的先抽离
test: /node_modules/, //将第三方模块抽离出来
chunks: 'initial',
minSize: 0,
minChunks: 2
}
}
}
}
}
webpack懒加载
- vue和react的懒加载其实都是通过import实现
//index.js
let button = document.createElement('button')
button.innerHTML = 'hello';
button.addEventListener('click',function(){
//import 是草案中的语法,由jsonp实现动态加载文件
//返回值是一个1promise对象
import('./source.js').then(data => {
console.log(data.default);
})
})
webpack热更新
module.exports = {
devServer: {
hot: true, //启动热更新操作
port: 3000
},
plugins: [
new webpack.NameModulesPlugin(), //告诉我们哪个模块热更新
new webpack.HotModuleReplacementPlugin() //热更新插件
]
}
//index.js文件
import str from './source';
if(module.hot){
module.hot.accept('./source', () => {
require('./source');
})
}
Tapable
- webpack本质上是一种事件流机制,它的工作流程是将各个插件串联起来,而实现这一切的核心是Tapable,Tapable是有点类似于nodeJs的events库,核心原理也是依赖于发布订阅模式。
webpack中使用vue
在普通网页中使用vue
//1.使用script标签,引入vue的包 (此包功能较全)
<scrpit src="./lib/vue.js"></srcipt>
//2.在index页面中,创建一个id为app的div容器
<div id="app"></div>
//3.通过new Vue得到一个vm实例
let vm = new Vue({
el: '#app'
})
在webpack中尝试使用vue
//注意:在webpack中,使用import Vue from 'vue' 导入的Vue构造函数功能不完整,只提供了runtime-only的形式,并无提供像网页中那样的使用方式;
import Vue from 'vue'
let vm = new Vue({
el: '#app'
})
解决:指定完整vue.js文件的路径
方式一:import Vue from './node_modules/vue/dist/vue.js';
方式二:import vue from 'vue'
在webpack.config.js文件中
module.exports = {
resolve: {
alias: { //修改vue时被导入时包的路径
"vue$": "vue/dist/vue.js"
}
}
}
cnpm i vue -S
cnpm i vue-loader vue-template-compiler -D
//webpack.config.js文件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
mode: "development",
entry: './src/index.js',
output: {
path: path.resolve(__dirname,'./dist'),
filename: 'bundle.js'
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),
new VueLoaderPlugin()
],
module: {
rules: [
{ test: /\.css/, use: ['style-loader','css-loader'] },
{ test: /\.vue/, use: 'vue-loader' }
]
}
}
1.安装 vue 包,cnpm i vue -S
2.由于在webpack中推荐使用vue组件模板文件定义组件,故需要安装能解析这种文件的loader cnpm i vue-loader vue-template-compiler -D
3.在main.js中导入vue模块, import Vue from 'vue'
4.定义.vue结尾的组件,由三部分组成:template script style
5.使用import login from './login.vue'导入这个组件
6.创建vm实例 let vm = new Vue({ el: '#app', render: c => c(login) })
7.在页面创建一个id为app的div元素,作为我们vm实例要控制的区域;
注意:App这个组件是通过vm实例的render函数渲染出来的,render函数如果要渲染组件,渲染出来的组件只能放到el:‘#app’ 所指定的元素中。
Account和Goodlist组件是通过路由配置监听到的,所以这两个组件只能展示到属于路由的中去。
包的查找规则
找vue文件
- 找项目根目录中有没有 node_modules 的文件夹
- 在node_modules中根据包名找对应的vue文件夹
- 在vue文件夹中,找一个叫package.json 的包配置文件
- 在 package.json 文件中,查找一个main 属性【main属性指定了这个包在被加载时的入口文件】
//sf-pack.js文件
//需要找到当前执行命令的路径,拿到webpack.config.json
const path = require('path')
//config配置文件
let config = require(path.resolve('webpack.config.js'));
let Compiler = require('../lib/Compiler.js');
let compiler = new Compiler(config);
//标识运行编译
compiler.run();
//Compiler.js文件
const path = require('path');
const fs = require('fs');
class Compiler{
constructor(config){
//entry output
this.config = config;
//需要保存入口文件的路径
this.entryId; //'./src/index.js'
//需要保存所有模块依赖
this.modules = {};
this.entry = config.entry; //入口路径
//工作路径
this.root = process.cwd();
}
getSource(modulePath){
let content = fs.readFileSync(modulePath,'utf-8');
return content;
}
//构建模块
buildModule(modulePath,isEntry){
//拿到模块内容
let source = this.getSource(modulePath);
//模块id modulePath = modulePath - this.root;
let modulePath = './' + path.relative(this.root,modulePath);
}
run(){
//执行并且创建模块的依赖关系
this.buildModule(path.resolve(this.root,this.entry),true);
//true表示为主模块
//发射一个文件,该文件即为打包后的文件
this.emitFile();
}
}
module.exports = Compiler