在开始以前,咱们先来看如下Webpack官网首页的图片,思考已下该图片表达的意义css
思考:在网页中,咱们常常会引入哪些常见的静态资源html
- JS
- .js .jsx .coffee .ts(TypeScript 类 C# 语言)
- CSS
- .css .less .sass .scss
- Images
- .jpg .png .gif .bmp .svg
- 字体文件(Fonts)
- .svg .ttf .eot .woff .woff2
- 模板文件
- .ejs .jade .vue【这是在webpack中定义组件的方式,推荐这么用】
问题:网页中静态资源多了之后存在的问题前端
- 网页加载速度变慢,由于要屡次重复的发送资源请求
- 要处理错综复杂的依赖关系
如何解决上述两个问题?vue
- 合并、压缩、精灵图、图片的Base64编码
- 可使用以前学过的requireJS、也可使用webpack能够解决各个包之间的复杂依赖关系;
Webpack是一个前端的项目构建工具,它是基于node.js开发出来的一个前端工具node
如何实现上述的2种解决方案?jquery
- 使用Gulp, 是基于 task 任务的;
- 使用Webpack, 是基于整个项目进行构建的;
- 借助于webpack这个前端自动化构建工具,能够完美实现资源的合并、打包、压缩、混淆等诸多功能。
- 根据官网的图片介绍webpack打包的过程
- webpack官网
Webpack支持如下规范webpack
//moduleA.js 导出
module.exports = function(){
//...
}
//moduleB.js 导入
var moduleA = require('./moduleA')
复制代码
//moduleA.js 导入和导出
define(['jquery','./math.js'],function($,math){
//AMD是依赖前置,将文件的依赖经过数组的形式导入,而后看成函数的参数传递进函数使用
//经过return来实现对外接口
return helloWorld
})
复制代码
CMD规范(推崇就近依赖,须要用到的时候再去加载模块)git
标准语法:define(id?,deps?,factory)github
- 一个文件一个模块,因此常常用文件名做为模块id
- CMD推崇依赖就近,因此通常不在define的参数中写依赖,在factory中写
- factory是一个函数,该函数拥有三个参数 function(require,exports,module)
- require:一个方法,接收模块标识,用来获取其它模块提供的接口
- exports:一个对象,用来向外提供模块接口
- module:一个对象,存储了与当前模块相关联的一些属性和方法
define(fcuntion(require,exports,module){
var $ = require('jquery.js')
})
复制代码
//moduleA.js 导出
//...内容区
//导出函数(还能够导出对象以及任何你想导出的数据类型)
exports.func = someFunc
//moduleB.js 导入
import func from './moduleA'
复制代码
npm i webpack -g
全局安装Webpack,这样就能在全局使用Webpack命令
webpack4.0
以上的版本,须要全局安装 Webpack-cli
npm i webpack --save-dev
安装到项目依赖中npm info webpack
复制代码
npm install webpack@版本号
复制代码
npm uninstall webpack webpack-cli -g
复制代码
目标:使用Webpack打包构建列表隔行变色案例
建立基本的目录结构
webpack-study
在项目目录下面运行npm init
初始化项目
使用 npm i jquery --save
安装jquery类库
建立main.js并书写各行变色的代码逻辑
main.js
//导入jquery内库
import $ from 'jquery'
$(function(){
$("li:odd").css('backgroundColor','red')
$("li:even").css('backgroundColor','tomato')
})
复制代码
在页面直接引用main.js会报错,由于浏览器不认识ES6的新语法import,须要使用Webpack进行处理,Webpack默认会把这种高级语法转换为低级浏览器可以识别的语法
运行 webpack 入口文件路径 输出文件路径
对main.js进行处理
webpack ./src/main.js ./dist/bundle.js
复制代码
在index.html中引入bundle.js代替main.js
问题描述:
webpack 入口文件路径 输出文件路径
来对文件进行处理,使用起来比较繁琐期待实现:
实现方法
const path = require('path')
module.exports = {
entry:path.join(__dirname,'./src/main.js'),
output:{
path:path.join(__dirname,'./dist'),
filename:'bundle.js'
}
}
复制代码
问题描述:每次改完代码,都须要手动执行webpack
命令打包编译文件,比较繁琐
目标:每次改完代码,咱们点击保存以后就能够帮咱们自动打包编译
安装 webpack-dev-server
npm install webpack-dev-server -D
复制代码
在 package.json
里面的 scripts
属性里面添加 webpack-dev-server
命令到开发环境
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server"
},
复制代码
在本地安装webpack,webpack-dev-server想要在本地项目中运行,必须在项目中也安装 webpack
npm install webpack -D
复制代码
执行 npm run dev
命令便可,会有以下返回
由第四步可知,webpack-dev-server帮咱们生成的bundle.js运行于项目根目录,这个文件并无存到物理磁盘上,而是托管到了电脑内存中,因此咱们在项目中根本看不到这个bundle.js文件,可是咱们能够经过将index.html中的bundle.js引用路径修改成项目根路径,便可引用到该文件
<script src="/bundle.js"></script>
复制代码
能够认为 webpack-dev-server
把打包好的文件,以一种虚拟的形式,托管到了项目的根目录中,虽然咱们看不到它,可是能够认为和 dist,src,nodemodule
,平级,有一个看不见的文件,叫作bundle.js
注意:
webpack-dev-server
除了帮咱们实现自动编译打包的功能以外,还能够添加 额外的参数帮咱们实现更强大的功能
--open
--port 3000
--contentBase src
--hot
//package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open --port 3000 --contentBase src --hot"
},
复制代码
//webpack.config.js
const webpack = require('webpack') //引入Webpack,启用热更新的第2步
devServer:{ //设置dev-server命令参数的第二种形式,相对麻烦一些
open:true,
port:3000,
contentBase:'src',
hot:true // 启用热更新的第一步
},
plugins:[ //配置插件的节点,热更新的第2步
new webpack.HotModuleReplacementPlugin() //new 一个热更新的模块对象,这是启用热更新的第3步
]
复制代码
插件做用:
插件用法:
安装 html-webpack-plugin插件
npm i html-webpack-plugin -D
复制代码
在webpack.config.js文件中添加插件
//webpack.config.js
const htmlWebpackPlugin = require('html-webpack-plugin')
plugins:[
new htmlWebpackPlugin({ //建立一个在内存中生成html页面的插件
template:path.join(__dirname,'./src/index.html'),//指定模版页面,未来会根据指定的页面路径,去生成内存中的页面
filename:'index.html'
})
]
复制代码
实际展现:
npm install module_name -S 即 npm install module_name –save 写入dependencies
npm install module_name -D 即 npm install module_name –save-dev 写入devDependencies
npm install module_name -g 全局安装(命令行使用)
npm install module_name 本地安装(将安装包放在 ./node_modules 下)
dependencies与devDependencies有什么区别呢?
注意:Webpack默认只能打包处理Js类型的文件,没法处理其它的非Js类型的文件
若是要处理非Js类型的文件,咱们须要手动安装一些合适的第三方loader加载器
打包处理CSS文件:安装 style-loader css-loader
npm i style-loader css-loader -D
复制代码
在webpack.config.js
配置文件里面新增module
节点对象,在这个module
对象身上,有个rules
属性数组,这个数组中,存放了全部的第三方文件爱你的匹配和处理规则
module:{ //这个节点,用来配置全部第三方模块加载器
rules:[ //配置第三方模块的匹配规则
{test:/\.css$/,use:['style-loader','css-loader']} //配置处理 .css文件的第三方loader规则
]
}
复制代码
在main.js入口文件里面引入CSS文件便可成功使用
import './css/index.css'
复制代码
问题描述:
Webpack
没法处理CSS
文件中的URL地址,不管是图片仍是字体库,只要是URL,都处理不了解决方法:
经过安装 url-loader file-loader
插件来对URL进行处理
npm i url-loader file-loader -D
复制代码
使用步骤
安装 url-loader file-loader
插件
在webpack.config.js
文件里面配置URL的处理规则
base64
格式的字符串, 若是 图片小于给定的 limit 值,则会被转为 base64
的字符串//URL图片路径的匹配规则
{test:/\.(jpg|jepg|png|gif)$/,use:'url-loader?limit=1000&name=[hash:8]-[name].[ext]'},
//字体图标的匹配规则
{test:/\.(eot|svg|woff|woff2|ttf)$/,use:'url-loader'}
复制代码
- 先校验文件类型,若是是js文件直接打包
- 若是非js文件,拿到后缀名,去webpack.config.js里面找对应匹配规则
- 找到则调用规则打包,不然报错
- rules的use规则数组从右到左调用,会将后面调用完毕的处理结果交给前面的规则继续处理
- 调用完毕以后会将处理结果直接交给Webpack进行打包合并,最终输出到bundle.js中去
示例:
css-loader
插件进行处理,将处理结果交给style-loader
继续处理module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
}
复制代码
因为Webpack
默认只能打包处理Js
类型的文件,没法处理其它的非Js
类型的文件,因此若是要处理非Js
类型的文件,咱们须要手动安装一些合适的第三方loader
加载器。
安装并使用第三方加载器的方法为如下几步
//添加module对象
//在module对象里面添加rules数组
//在rules数组里面添加相对应文件的匹配规则
module:{
rules:[
{test:/\.css$/,use:['style-loader','css-loader']}
]
}
复制代码
CSS
或者其它文件,都以import
命令的方式在入口js
文件里面引入完成以上步骤便可成功启用loader加载器
在
Webpack
中,默认只能处理一部分ES6
语法,一些更高级的ES6
或者ES7
语法,Webpack
是处理不了的,这时候,就须要借助第三方loader,来帮助Webpack
处理这些高级的语法,当第三方loader把高级语法转为低级的语法以后,会把结果交给Webpack
去打包到bundle.js
中经过Babel,能够帮助咱们将高级语法转换为低级的语法
cnpm i babel-core babel-loader babel-plugin-transform-runtime -D
cnpm i bebel-preset-env babel-preset-stage-0 -D
在Webpack.config.js配置文件中,在module节点下的rules数组中添加一个新的匹配规则
注意:
{test:/\.js$/,use:'babel-loader',exclude:/node_modules/}
复制代码
在项目的根目录中,新建一个叫作 .babelrc
的Babel配置文件
json
格式,必须复核JSON
语法规范,不能写注释,字符串必须使用双引号babel-plugin-transfrom
babel-preset-env,babel-preset-stage-0
{
"presets":["env","stage-0"],
"plugins":["transform-runtime"]
}
复制代码
完成以上配置,便可在项目中使用ES6语法,能够编写一个Class类,设置静态属性并打印它们来测试
传统的Vue使用方式
- 经过 script标签引入 vue.js文件
- 在body中声明一个盒子,而且为它设置一个id属性
- 在script标签中经过new Vue()建立一个VM实例
当咱们使用 import命令导入模块中的一个包的时候,它的查找规则是怎样的呢
- 找项目目录中有没有 node_modules 的文件夹
- 在node_modules文件夹中找到对应的包名
- 在package.json的配置文件里面,找到一个main属性【main属性指定了这个包在被加载的时候的入口文件】
接下来咱们来试着导入一个vue包,并查看它对应的入口文件
安装 npm i vue -D
导入包 import Vue form 'vue'
查看 node_modules/vue/package.json中的main属性,看它的入口文件是哪一个
"main": "dist/vue.runtime.common.js",
复制代码
仔细回想一下,咱们平时导入的是这个文件吗?显然不是,咱们平时导入的是vue.js文件,那这里为何会不同呢?带着这个疑问,咱们继续
以下都是一些简单的代码模版,这里用代码说明,不作具体解释
//main.js 入口文件
//此处导入的是 vue.runtime.common.js
import Vue from 'vue'
var login = {
template:`<h1>Login success</h1>`
}
var vm = new Vue({
el:'#app',
data () {
return {
msg:'hello'
}
},
components:{
login
}
})
复制代码
//index.html
<body>
<div id="app">
<p>{{msg}}</p>
<login></login>
</div>
</body>
复制代码
运行,查看结果
错误分析
将模版预编译为渲染函数(使用render函数渲染模版)
渲染函数的使用,详情请看这里:Render函数的使用
经过尝试,发现把组件写在入口文件里面并不能达到目标,因而,咱们抽取vue模版,经过vue文件来建立模版
建立 login.vue文件
<template>
<div>
<h1>
这是经过 vue 文件建立的模版
</h1>
</div>
</template>
<script>
</script>
<style>
</style>
复制代码
安装加载.vue文件的插件,并配置webpack.config.js
cnpm i vue-loader vue-template-compiler -D
复制代码
//webpack.config.js
const vueLoaderPlugin =require('vue-loader/lib/plugin')
plugins:[
new vueLoaderPlugin()
],
module:{
rules:[
{test:/\.vue$/,use:'vue-loader'}, //处理.vue文件
]
},
复制代码
在入口文件里面引入 login.vue,并使用render函数渲染
import Vue from 'vue'
import login from './login.vue'
var vm = new Vue({
el:'#app',
data () {
return {
msg:'hello'
}
},
// render:function(createElement){
// return createElement(login)
// },
//简写
render:c => c(login)
})
复制代码
再次运行,发现运行成功
用包含编译器的构建(即完整的vue.js文件)
修改原始导入的vue.runtime.common.js,将引入的包修改成vue.js
import Vue from '../node_modules/vue/dist/vue.js'
import Vue form 'vue'
,可是须要在 webpack.config.js文件中增长以下配置resolve:{
alias:{
"vue$":"vue/dist/vue.js"
}
}
复制代码
npm i vue -S
npm i vue-loader vue-template-complier -D
Import Vue from 'vue'
Import login from './login.vue'
命令导入这个组件安装 vue-router依赖模块
npm i vue -S
npm i vue-router -S
复制代码
在main.js中引入Vue 和 vue-router模块,并显示的调用vue-router
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
复制代码
在src目录下建立App.vue主页面模版文件
<template>
<div>
<h1>APP组件</h1>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
复制代码
在 index文件中 建立一个 id 属性等于 app
的div元素
在src
目录下面新建文件夹,并在新建的文件夹下面建立login.vue
和register.vue
文件
将login.vue
和register.vue
组件应用到app
主页模板上面去
<template>
<div>
<h1>APP组件</h1>
<router-link to='/login'>Login</router-link>
<router-link to='/register'>Register</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
复制代码
在main.js
文件中配置路由,建立VM
实例,挂载app
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import app from './App.vue'
import login from './comp1/login.vue'
import register from './comp1/register.vue'
var router = new VueRouter({
routes:[
{path:'/login',component:login},
{path:'/register',component:register}
]
})
var vm = new Vue({
el:'#app',
render: c => c(app),
router
})
复制代码
完成以上配置,启动项目便可
在Webpack中使用路由的嵌套和在Vue中使用路由的嵌套基本相似
在路由基本使用的前提下,建立两个新的组件 account,money
在login组件中添加这两个组件的 ,并添加容器
<template>
<div>
<h1>Login success</h1>
<router-link to='/login/account'>Account</router-link>
<router-link to='/login/money'>Money</router-link>
<router-view></router-view>
</div>
</template>
复制代码
在路由配置中给login添加children属性,配置account,money路由便可
var router = new VueRouter({
routes:[
{path:'/login',component:login,children:[
{path:'account',component:account},
{path:'money',component:money},
]},
{path:'/register',component:register}
]
})
复制代码
为避免入口文件 main.js里面的内容过多,咱们推荐使用抽离路由的方式,为router单首创建一个文件夹
在src目录下建立 router.js
import VueRouter from 'vue-router'
import login from './comp1/login.vue'
import register from './comp1/register.vue'
import account from './comp2/account.vue'
import money from './comp2/money.vue'
var router = new VueRouter({
routes:[
{path:'/login',component:login,children:[
{path:'account',component:account},
{path:'money',component:money},
]},
{path:'/register',component:register}
]
})
export default router
复制代码
在main.js入口文件中,引入router.js暴露router对象,挂载到app便可
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import app from './App.vue'
import router from './router.js'
var vm = new Vue({
el:'#app',
render: c => c(app),
router
})
复制代码
scoped属性:
lang属性:
在 .vue文件中,默认只能使用普通的 css样式语法,若是想要使用 scss,或者 less语法样式,须要给style标签设置 lang属性,而且指定具体的使用语法
<style lang='less'>
</style>
复制代码