本质上是一些函数的集合。每次调用函数,实现一个特定的功能,接着把控制权交给使用者 框架,是一套完整的解决方案,使用框架的时候,需要把你的代码放到框架合适的地方,框架会在合适的时机调用你的代码 虽然没有完全遵循 MVVM 模型,Vue 的设计无疑受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的简称) 这个变量名表示 Vue 实例 生命周期钩子函数的定义:从组件被创建,到组件挂载到页面上运行,再到页面关闭组件被卸载,这三个阶段总是伴随着组件各种各样的事件,这些事件,统称为组件的生命周期函数! 钩子函数 - beforeCreate() 钩子函数 - created() 钩子函数 - beforeMounted() 钩子函数 - mounted() 钩子函数 - beforeUpdated() 钩子函数 - updated() 钩子函数 - beforeDestroy() 钩子函数 - destroyed() Get 请求 Post 请求 全局配置 拦截器 组件 组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树 is特性 在某些特定的标签中只能存在指定表恰 如ul > li 如果要浏览器正常解析则需要使用is 父组件到子组件 方式:父组件给子组件传递一个函数,由子组件调用这个函数 步骤: 在简单的场景下,可以使用一个空的 Vue 实例作为事件总线 案例: SPA: Single Page Application 优势 实现思路和技术点 路由 路由其他配置 路由参数 嵌套路由 - 子路由 为什么需要模块化 模块的概念 模块化解决的问题 JS实现模块化的规范 Asynchronous Module Definition:异步模块定义,浏览器端模块开发的规范 代表:require.js 特点:模块被异步加载,模块加载不影响后面语句的运行 1、定义模块 2、加载模块 3、路径查找配置 4、非模块化和依赖项支持 5、路径加载规则 概述 webpack 是一个现代 JavaScript 应用程序的模块打包器(特点 module、 bundler) webpack 是一个模块化方案(预编译) webpack获取具有依赖关系的模块,并生成表示这些模块的静态资源 webpack起源 webpack与模块 webpack文档和资源 安装webpack webpack的基本使用 命令行方式演示 - 案例:隔行变色 一、package.json配置方式 二、webpack.config.js 配置方式(推荐) webpack enables use of loaders to preprocess files. This allows you to bundle any static resource way beyond JavaScript. 1、 CSS打包 2、 使用webpack打包sass文件 3、 图片和字体打包 babel 基本使用(两步) babel的说明 babel-preset-* Babel通过语法转换器,能够支持最新版本的JavaScript语法 babel-preset-* 用来指定我们书写的是什么版本的JS代码 babel-polyfill 和 transform-runtime 总结 vue单文件组件 使用单文件组件 单文件组件+路由 快速开始 创建项目发布配置文件 打包处理过程 处理图片路径 自动删除dist目录 分离第三方包 压缩HTML页面 步骤介绍
Framework
主要区别
MVVM的介绍
MVC
MVVM组成
优势对比
Vue中的MVVM
学习Vue要转化思想
起步 - Hello Vue
<!-- 指定vue管理内容区域,需要通过vue展示的内容都要放到找个元素中 通常我们也把它叫做边界 数据只在边界内部解析-->
<div id="app">{{ msg }}</div>
<!-- 引入 vue.js -->
<script src="vue.js"></script>
<!-- 使用 vue -->
<script>
var vm = new Vue({
// el:提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标
el: '#app',
// Vue 实例的数据对象,用于给 View 提供数据
data: {
msg: 'Hello Vue'
}
})
</script>
Vue实例
var vm = new Vue({
data: {
msg: '大家好,...'
}
})
vm.$data.msg === vm.msg // true
数据绑定
<h1>Hello, {{ msg }}.</h1>
<p>{{ 1 + 2 }}</p>
<p>{{ isOk ? 'yes': 'no' }}</p>
<!-- !!!错误示范!!! -->
<h1 title="{{ err }}"></h1>
双向数据绑定 Vue two way data binding
/* defineProperty语法 介绍 */
var obj = {}
Object.defineProperty(obj, 'msg', {
// 设置 obj.msg = "1" 时set方法会被系统调用 参数分别是设置后和设置前的值
set: function (newVal, oldVal) { },
// 读取 obj.msg 时get方法会被系统调用
get: function ( newVal, oldVal ) {}
})
Vue双向绑定的极简实现
<!-- 示例 -->
<input type="text" id="txt" />
<span id="sp"></span>
<script>
var txt = document.getElementById('txt'),
sp = document.getElementById('sp'),
obj = {}
// 给对象obj添加msg属性,并设置setter访问器
Object.defineProperty(obj, 'msg', {
// 设置 obj.msg 当obj.msg反生改变时set方法将会被调用
set: function (newVal) {
// 当obj.msg被赋值时 同时设置给 input/span
txt.value = newVal
sp.innerText = newVal
}
})
// 监听文本框的改变 当文本框输入内容时 改变obj.msg
txt.addEventListener('keyup', function (event) {
obj.msg = event.target.value
})
</script>
动态添加数据的注意点
var vm = new Vue({
data: {
stu: {
name: 'jack',
age: 19
}
}
})
/* Vue.set */
Vue.set(vm.stu, 'gender', 'male')
/* Object.assign 将参数中的所有对象属性和值 合并到第一个参数 并返回合并后的对象*/
vm.stu = Object.assign({}, vm.stu, { gender: 'female', height: 180 })
异步DOM更新
methods: {
fn() {
this.msg = 'change'
this.$nextTick(function () {
console.log('$nextTick中打印:', this.$el.children[0].innerText);
})
console.log('直接打印:', this.$el.children[0].innerText);
}
}
指令
v-text
<h1 v-text="msg"></h1>
v-html
<h1 v-html="msg"></h1>
v-bind
<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>
v-on
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>
事件修饰符
v-model
<input type="text" v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
v-for
<!-- 1 基础用法 -->
<div v-for="item in items">
{{ item.text }}
</div>
<!-- item 为当前项,index 为索引 -->
<p v-for="(item, index) in list">{{item}} -- {{index}}</p>
<!-- item 为值,key 为键,index 为索引 -->
<p v-for="(item, key, index) in obj">{{item}} -- {{key}}</p>
<p v-for="item in 10">{{item}}</p>
key属性
<div v-for="item in items" :key="item.id">
<!-- 内容 -->
</div>
样式处理 -class和style
<!-- 1 -->
<div v-bind:class="{ active: true }"></div> ===> 解析后
<div class="active"></div>
<!-- 2 -->
<div :class="['active', 'text-danger']"></div> ===>解析后
<div class="active text-danger"></div>
<!-- 3 -->
<div v-bind:class="[{ active: true }, errorClass]"></div> ===>解析后
<div class="active text-danger"></div>
--- style ---
<!-- 1 -->
<div v-bind:style="{ color: activeColor, 'font-size': fontSize + 'px' }"></div>
<!-- 2 将多个 样式对象 应用到一个元素上-->
<!-- baseStyles 和 overridingStyles 都是data中定义的对象 -->
<div v-bind:style="[baseStyles, overridingStyles]"></div>
v-if 和 v-show
<p v-show="isShow">这个元素展示出来了吗???</p>
<p v-if="isShow">这个元素,在HTML结构中吗???</p>
提升性能:v-pre
<span v-pre>{{ this will not be compiled }}</span>
提升性能:v-once
<span v-once>This will never change: {{msg}}</span>
过滤器 filter
全局过滤器
Vue.filter('filterName', function (value) {
// value 表示要过滤的内容
})
<div>{{ dateStr | date }}</div>
<div>{{ dateStr | date('YYYY-MM-DD hh:mm:ss') }}</div>
<script>
Vue.filter('date', function(value, format) {
// value 要过滤的字符串内容,比如:dateStr
// format 过滤器的参数,比如:'YYYY-MM-DD hh:mm:ss'
})
</script>
局部过滤器
{
data: {},
// 通过 filters 属性创建局部过滤器
// 注意:此处为 filters
filters: {
filterName: function(value, format) {}
}
}
按键值修饰符
// 只有在 keyCode 是 13 时调用 vm.submit()
@keyup.13="submit"
// 使用全局按键别名
@keyup.enter="add"
---
// 通过全局 config.keyCodes 对象自定义键值修饰符别名
Vue.config.keyCodes.f2 = 113
// 使用自定义键值修饰符
@keyup.enter.f2="add"
监视数据变化 - watch
new Vue({
data: { a: 1, b: { age: 10 } },
watch: {
a: function(val, oldVal) {
// val 表示当前值
// oldVal 表示旧值
console.log('当前值为:' + val, '旧值为:' + oldVal)
},
// 监听对象属性的变化
b: {
handler: function (val, oldVal) { /* ... */ },
// deep : true表示是否监听对象内部属性值的变化
deep: true
},
// 只监视user对象中age属性的变化
'user.age': function (val, oldVal) {
},
}
})
计算属性
var vm = new Vue({
el: '#app',
data: {
firstname: 'jack',
lastname: 'rose'
},
computed: {
fullname() {
return this.firstname + '.' + this.lastname
}
}
})
实例生命周期
生命周期介绍
axios
// 在浏览器中使用,直接引入js文件使用下面的GET/POST请求方式即可
// 1 引入 axios.js
// 2 直接调用axios提供的API发送请求
created: function () {
axios.get(url)
.then(function(resp) {})
}
---
// 配合 webpack 使用方式如下:
import Vue from 'vue'
import axios from 'axios'
// 将 axios 添加到 Vue.prototype 中
Vue.prototype.$axios = axios
---
// 在组件中使用:
methods: {
getData() {
this.$axios.get('url')
.then(res => {})
.catch(err => {})
}
}
---
// API使用方式:
axios.get(url[, config])
axios.post(url[, data[, config]])
axios(url[, config])
axios(config)
const url = 'http://vue.studyit.io/api/getnewslist'
// url中带有query参数
axios.get('/user?id=89')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// url和参数分离,使用对象
axios.get('/user', {
params: {
id: 12345
}
})
// 使用 qs 包,处理将对象序列化为字符串
// npm i -S qs
// var qs = require('qs')
import qs from 'qs'
qs.stringify({ 'bar': 123 }) ===> "bar=123"
axios.post('/foo', qs.stringify({ 'bar': 123 }))
// 或者:
axios.post('/foo', 'bar=123&age=19')
const url = 'http://vue.studyit.io/api/postcomment/17'
axios.post(url, 'content=点个赞不过份')
axios.post('/user', qs.stringify({
firstName: 'Fred',
lastName: 'Flintstone'
}))
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 设置请求公共路径:
axios.defaults.baseURL = 'http://vue.studyit.io'
// 请求拦截器
axios.interceptors.request.use(function (config) {
// 所有请求之前都要执行的操作
return config;
}, function (error) {
// 错误处理
return Promise.reject(error);
});
// 响应拦截器
axios.interceptors.response.use(function (response) {
// 所有请求完成后都要执行的操作
return response;
}, function (error) {
// 错误处理
return Promise.reject(error);
});
自定义指令
全局自定义指令
// 第一个参数:指令名称
// 第二个参数:配置对象,指定指令的钩子函数
Vue.directive('directiveName', {
// bind中只能对元素自身进行DOM操作,而无法对父级元素操作
// 只调用一次 指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
bind( el,binding, vnode ) {
// 参数详解
// el:指令所绑定的元素,可以用来直接操作 DOM 。
// binding:一个对象,包含以下属性:
// name:指令名,不包括 v- 前缀。
// value:指令的绑定值,等号后面的值 。
// oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
// expression:字符串形式的指令表达式 等号后面的字符串 形式
// arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
// modifiers:指令修饰符。例如:v-directive.foo.bar中,修饰符对象为 { foo: true, bar: true }。
// vnode:Vue 编译生成的虚拟节点。。
// oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
},
// inserted这个钩子函数调用的时候,当前元素已经插入页面中了,也就是说可以获取到父级节点了
inserted ( el,binding, vnode ) {},
// DOM重新渲染前
update(el,binding, vnode,oldVnode) {},
// DOM重新渲染后
componentUpdated ( el,binding, vnode,oldVnode ) {},
// 只调用一次,指令与元素解绑时调用
unbind ( el ) {
// 指令所在的元素在页面中消失,触发
}
})
// 简写 如果你想在 bind 和 update 时触发相同行为,而不关心其它的钩子:
Vue.directive('自定义指令名', function( el, binding ) {})
// 例:
Vue.directive('color', function(el, binding) {
el.style.color = binging.value
})
// 使用 注意直接些会被i成data中的数据“red” 需要字符串则嵌套引号"'red'"
<p v-color="'red'"></p>
局部自定义指令
var vm = new Vue({
el : "#app",
directives: {
directiveName: { }
}
})
全局组件
// 1 注册全局组件
Vue.component('my-component', {
// template 只能有一个根元素
template: '<p>A custom component!</p>',
// 组件中的 `data` 必须是函数 并且函数的返回值必须是对象
data() {
return {
msg: '注意:组件的data必须是一个函数!!!'
}
}
})
// 2 使用:以自定义元素的方式
<div id="example">
<my-component></my-component>
</div>
// =====> 渲染结果
<div id="example">
<p>A custom component!</p>
</div>
// 3 template属性的值可以是:
- 1 模板字符串
- 2 模板id template: '#tpl'
<script type="text/x-template" id="tpl">
<p>A custom component!</p>
</script>
// 注册组件,传入一个扩展过的构造器
Vue.component('my-component', Vue.extend({ /* ... */ }))
// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })
var Home = Vue.extend({
template: '',
data() {}
})
Vue.component('home', Home)
局部组件
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// 注意:此处为 components
components: {
// <my-component> 将只在当前vue实例中使用
// my-component 为组件名 值为配置对象
'my-component': {
template: ``,
data () { return { } },
props : []
}
}
})
<!-- 案例 -->
<ul id="app">
<!-- 不能识别 -->
<my-li></my-li>
正常识别
<li is="my-li"></li>
</ul>
<script>
var vm = new Vue({
el: "#app",
components : {
myLi : {
template : `<li>内容</li>`
}
}
})
</script>
组件通讯
<div id="app">
<!-- 如果需要往子组件总传递父组件data中的数据 需要加v-bind="数据名称" -->
<hello v-bind:msg="info"></hello>
<!-- 如果传递的是字面量 那么直接写-->
<hello my-msg="abc"></hello>
</div>
<!-- js -->
<script>
new Vue({
el: "#app",
data : {
info : 15
},
components: {
hello: {
// 创建props及其传递过来的属性
props: ['msg', 'myMsg'],
template: '<h1>这是 hello 组件,这是消息:{{msg}} --- {{myMsg}}</h1>'
}
}
})
</script>
子组件到父组件
<hello @pfn="parentFn"></hello>
<script>
Vue.component('hello', {
template: '<button @click="fn">按钮</button>',
methods: {
// 子组件:通过$emit调用
fn() {
this.$emit('pfn', '这是子组件传递给父组件的数据')
}
}
})
new Vue({
methods: {
// 父组件:提供方法
parentFn(data) {
console.log('父组件:', data)
}
}
})
</script>
非父子组件通讯
var bus = new Vue()
// 在组件 B 绑定自定义事件
bus.$on('id-selected', function (id) {
// ...
})
// 触发组件 A 中的事件
bus.$emit('id-selected', 1)
<!-- 组件A: -->
<com-a></com-a>
<!-- 组件B: -->
<com-b></com-b>
<script>
// 中间组件
var bus = new Vue()
// 通信组件
var vm = new Vue({
el: '#app',
components: {
comB: {
template: '<p>组件A告诉我:{{msg}}</p>',
data() {
return {
msg: ''
}
},
created() {
// 给中间组件绑定自定义事件 注意:如果用到this 需要用箭头函数
bus.$on('tellComB', (msg) => {
this.msg = msg
})
}
},
comA: {
template: '<button @click="emitFn">告诉B</button>',
methods: {
emitFn() {
// 触发中间组件中的自定义事件
bus.$emit('tellComB', '土豆土豆我是南瓜')
}
}
}
}
})
</script>
内容分发
<!-- html代码 -->
<div id="app">
<hello>
<!-- 如果只有一个slot插槽 那么不需要指定名称 -->
<p slot="插槽名称">我是额外的内容</p>
</hello>
</div>
// js代码
new vue({
el : "#app",
components : {
hello : {
template : `
<div>
<p>我是子组件中的内容</p>
<slot name="名称"></slot>
</div>
`
}
}
})
获取组件(或元素) - refs
<div id="app">
<div ref="dv"></div>
<my res="my"></my>
</div>
<!-- js -->
<script>
new Vue({
el : "#app",
mounted() {
this.$refs.dv //获取到元素
this.$refs.my //获取到组件
},
components : {
my : {
template: `<a>sss</a>`
}
}
})
</script>
SPA -单页应用程序
单页Web应用(single page application,SPA),就是只有一个Web页面的应用,
是加载单个HTML页面,并在用户与应用程序交互时动态更新该页面的Web应用程序。
基本使用
<div id="app">
<!-- 5 路由入口 指定跳转到只定入口 -->
<router-link to="/home">首页</router-link>
<router-link to="/login">登录</router-link>
<!-- 7 路由出口:用来展示匹配路由视图内容 -->
<router-view></router-view>
</div>
<!-- 1 导入 vue.js -->
<script src="./vue.js"></script>
<!-- 2 导入 路由文件 -->
<script src="./node_modules/vue-router/dist/vue-router.js"></script>
<script>
// 3 创建两个组件
const Home = Vue.component('home', {
template: '<h1>这是 Home 组件</h1>'
})
const Login = Vue.component('login', {
template: '<h1>这是 Login 组件</h1>'
})
// 4 创建路由对象
const router = new VueRouter({
routes: [
// 路径和组件一一对应
{ path: '/home', component: Home },
{ path: '/login', component: Login }
]
})
var vm = new Vue({
el: '#app',
// 6 将路由实例挂载到vue实例
router
})
</script>
重定向
// 将path 重定向到 redirect
{ path: '/', redirect: '/home' }
new Router({
routers:[],
mode: "hash", //默认hash | history 可以达到隐藏地址栏hash值 | abstract,如果发现没有浏览器的 API 则强制进入
linkActiveClass : "now" //当前匹配的导航链接将被自动添加now类
})
// 方式一
<router-link to="/user/1001">如果你需要在模版中使用路由参数 可以这样 {{$router.params.id}}</router-link>
// 方式二
<router-link :to="{path:'/user',query:{name:'jack',age:18}}">用户 Rose</router-link>
<script>
// 路由
var router = new Router({
routers : [
// 方式一 注意 只有/user/1001这种形式能被匹配 /user | /user/ | /user/1001/ 都不能被匹配
// 将来通过$router.params获取参数返回 {id:1001}
{ path: '/user/:id', component: User },
// 方式二
{ path: "user" , component: User}
]
})
// User组件:
const User = {
template: `<div>User {{ $route.params.id }}</div>`
}
</script>
<!-- 如果要子啊vue实例中获取路由参数 则使用this.$router.params 获取路由参数对象 -->
<!-- {{$router.query}} 获取路由中的查询字符串 返回对象 -->
// 父组件:
const User = Vue.component('user', {
template: `
<div class="user">
<h2>User Center</h2>
<router-link to="/user/profile">个人资料</router-link>
<router-link to="/user/posts">岗位</router-link>
<!-- 子路由展示在此处 -->
<router-view></router-view>
</div>
`
})
// 子组件[简写]
const UserProfile = {
template: '<h3>个人资料:张三</h3>'
}
const UserPosts = {
template: '<h3>岗位:FE</h3>'
}
// 路由
var router =new Router({
routers : [
{ path: '/user', component: User,
// 子路由配置:
children: [
{
// 当 /user/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 当 /user/posts 匹配成功
// UserPosts 会被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
})
前端模块化
AMD 的使用
// 语法:define(name, dependencies?, factory);
// name表示:当前模块的名称,是一个字符串 可有可无
// dependencies表示:当前模块的依赖项,是一个数组无论依赖一项还是多项 无则不写
// factory表示:当前模块要完成的一些功能,是一个函数
// 定义对象模块
define({})
// 定义方法模块
define(function() {
return {}
})
// 定义带有依赖项的模块
define(['js/a'], function() {})
// - 注意:require的第一个参数必须是数组
// 参数必须是数组 表示模块路径 以当前文件为基准,通过回调函数中的参数获取加载模块中的变量 参数与模块按照顺序一一对应
require(['a', 'js/b'], function(a, b) {
// 使用模块a 和 模块b 中的代码
})
// 配置示例
// 注意配置应当在使用之前
require.config({
baseUrl: './js' // 配置基础路径为:当前目录下的js目录
})
require(['a']) // 查找 基础路径下的 ./js/a.js
// 简化加载模块路径
require.config({
baseUrl: './js',
// 配置一次即可,直接通过路径名称(template || jquery)加载模块
paths: {
template: 'assets/artTemplate/template-native',
jquery: 'assets/jquery/jquery.min'
}
})
// 加载jquery template模块
require(['jquery', 'template'])
// 示例
require.config({
baseUrl: './js',
paths: {
// 配置路径
noModule: 'assets/demo/noModule'
},
// 配置不符合规范的模块项
shim: {
// 模块名称
noModule: {
deps: [], // 依赖项
exports: 'sayHi' // 导出模块中存在的函数或变量
}
}
});
// 注意点 如果定义模块的时候,指定了模块名称,需要使用该名称来引用模块
// 定义 这个模块名称与paths中的名称相同
define('moduleA', function() {})
// 导入
require.config({
paths: {
// 此处的模块名:moduleA
moduleA: 'assets/demo/moduleA'
}
})
<!--
设置data-main属性
1 data-main属性指定的文件也会同时被加载
2 用于指定查找其他模块的基础路径
-->
<script src="js/require.js" data-main="js/main"></script>
Webpack
1 webpack 将带有依赖项的各个模块打包处理后,变成了独立的浏览器能够识别的文件
2 webpack 合并以及解析带有依赖项的模块
对比
模块化方案: webpack 和 requirejs(通过编写代码的方式将前端的功能,划分成独立的模块)
browserify 是与 webpack 相似的模块化打包工具
webpack 预编译 (在开发阶段通过webpack进行模块化处理, 最终项目上线, 就不在依赖于 webpack)
requirejs 线上的编译( 代码运行是需要依赖与 requirejs 的 )
/*
src/js/index.js
*/
// 1 导入 jQuery
import $ from 'jquery'
// 2 获取页面中的li元素
const $lis = $('#ulList').find('li')
// 3 隔行变色
// jQuery中的 filter() 方法用来过滤jquery对象
$lis.filter(':odd').css('background-color', '#def')
$lis.filter(':even').css('background-color', 'skyblue')
//命令行运行 `webpack src/js/index.js dist/bundle.js 目录生成在命令行运行目录
/*
运行流程:
1、webpack 根据入口找到入口文件
2、分析js中的模块化语法
3、将所有关联文件 打包合并输出到出口
*/
webpack-dev-server 配置
// 参数解释 注意参数是无序的 有值的参数空格隔开
// --open 自动打开浏览器
// --contentBase ./ 指定浏览器 默认打开的页面路径中的 index.html 文件
// --open 自动打开浏览器
// --port 8080 端口号
// --hot 热更新,只加载修改的文件(按需加载修改的内容),而非全部加载
"scripts": {
"dev": "webpack-dev-server --open --contentBase ./ --port 8080 --hot"
}
var path = require('path')
module.exports = {
// 入口文件
entry: path.join(__dirname, 'src/js/index.js'),
// 输出文件
output: {
path: path.join(__dirname, 'dist'), // 输出文件的路径
filename: 'bundle.js' // 输出文件的名称
}
}
const webpack = require('webpack')
devServer: {
// 服务器的根目录 Tell the server where to serve content from
// https://webpack.js.org/configuration/dev-server/#devserver-contentbase
contentBase: path.join(__dirname, './'),
// 自动打开浏览器
open: true,
// 端口号
port: 8888,
// --------------- 1 热更新 -----------------
hot: true
},
plugins: [
// ---------------- 2 启用热更新插件 ----------------
new webpack.HotModuleReplacementPlugin()
]
/* webpack.config.js */
const htmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new htmlWebpackPlugin({
// 模板页面路径
template: path.join(__dirname, './index.html'),
// 在内存中生成页面路径,默认值为:index.html
filename: 'index.html'
})
]
Loaders(加载器)
/* 在index.js 导入 css 文件*/
import './css/app.css'
/* webpack.config.js 配置各种资源文件的loader加载器*/
module: {
// 配置匹配规则
rules: [
// test 用来配置匹配文件规则(正则)
// use 是一个数组,按照从后往前的顺序执行加载
{test: /\.css$/, use: ['style-loader', 'css-loader']},
]
}
/* webpack.config.js */
// 参考:https://webpack.js.org/loaders/sass-loader/#examples
// "style-loader" :creates style nodes from JS strings 创建style标签
// "css-loader" :translates CSS into CommonJS 将css转化为CommonJS代码
// "sass-loader" :compiles Sass to CSS 将Sass编译为css
module:{
rules:[
{test: /\.(scss|sass)$/, use: ['style-loader', 'css-loader', 'sass-loader']},
]
}
/* webpack.config.js */
module: {
rules:[
// 打包 图片文件
{ test: /\.(jpg|png|gif|jpeg)$/, use: 'url-loader' },
// 打包 字体文件
{ test: /\.(woff|woff2|eot|ttf|otf)$/, use: 'file-loader' }
]
}
图片打包细节
/* webpack.config.js */
module: {
rules: [
// {test: /\.(jpg|png|gif|jpeg)$/, use: 'url-loader?limit=100'},
{
test: /\.(jpg|png|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
字体文件打包说明
/* webpack.config.js */
module: {
rules: [
// exclude 排除,不需要编译的目录,提高编译速度
{test: /\.js$/, use: 'babel-loader', exclude: /node_modules/}
]
}
/* 创建 .babelrc 文件*/
// 将来babel-loader运行的时候,会检查这个配置文件,并读取相关的语法和插件配置
{
"presets": ["env"]
}
Stage 0 - Strawman(展示阶段)
Stage 1 - Proposal(征求意见阶段)
Stage 2 - Draft(草案阶段)
Stage 3 - Candidate(候选人阶段)
Stage 4 - Finished(定案阶段)
Stage 0 is "i've got a crazy idea",
stage 1 is "this idea might not be stupid",
stage 2 is "let's use polyfills and transpilers to play with it",
stage 3 is "let's let browsers implement it and see how it goes",
stage 4 is "now it's javascript".
区别:
polyfill 所有兼容性问题,都可以通过polyfill解决(包括:实例方法)、污染全局环境
runtime 除了实例方法以外,其他兼容新问题都能解决、不污染全局环境
polyfill:如果想要支持全局对象(比如:`Promise`)、静态方法(比如:`Object.assign`)或者**实例方法**(比如:`String.prototype.padStart`)等,那么就需要使用`babel-polyfill`
babel-runtime :提供了兼容旧环境的函数,使用的时候,需要我们自己手动引入
比如: const Promise = require('babel-runtime/core-js/promise')
存在的问题:
1 手动引入太繁琐
2 多个文件引入同一个helper(定义),造成代码重复,增加代码体积
babel-plugin-transform-runtime:
1 自动引入helper(比如,上面引入的 Promise)
2 babel-runtime提供helper定义,引入这个helper即可使用,避免重复
3 依赖于 babel-runtime 插件
transform-runtime插件的使用:
直接在 .bablerc 文件中,添加一个 plugins 的配置项即可!!!
"plugins": [
"transform-runtime"
]
/*
babel-polyfill 的使用步骤:
1 main.js
*/
// 第一行引入
require("babel-polyfill")
var s = 'abc'.padStart(4)
console.log(s)
// 2 webpack.config.js 配置
module.exports = {
entry: ['babel-polyfill', './js/main.js']
}
babel-core babel核心包
babel-loader 用来解析js文件
babel-preset-* 新ES语法的解析和转换
transform-runtime / babel-polyfill 兼容旧浏览器,到达支持新API目的
// 判断浏览器是否兼容 padStart 这个 API
if (!String.prototype.padStart) {
// 如果不兼容, 就自己模拟 padStart的功能实现一份
String.prototype.padStart = function padStart(targetLength,padString) {
}
}
<!-- App.vue 示例代码: -->
<template>
<div>
<h1>VUE 单文件组件示例 -- App.vue</h1>
<p>这是 模板内容</p>
</div>
</template>
<script>
// 组件中的逻辑代码
export default {}
</script>
<style>
/* 组件样式 */
h1 {
color: red;
}
</style>
// webpack.config.js 配置:
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
}
/* main.js */
import Vue from 'vue'
// 导入 App 组件
import App from './App.vue'
const vm = new Vue({
el: '#app',
// 通过 render 方法,渲染App组件
render: c => c(App)
})
单文件组件使用步骤
import Vue from 'vue'
import App from './App.vue'
// ------------- vue路由配置 开始 --------------
import Home from './components/home/Home.vue'
import Login from './components/login/Login.vue'
// 1 导入 路由模块
import VueRouter from 'vue-router'
// 2 ** 调用use方法使用插件 **
Vue.use(VueRouter)
// 3 创建路由对象
const router = new VueRouter({
routes: [
{ path: '/home', component: Home },
{ path: '/login', component: Login }
]
})
// ------------- vue路由配置 结束 --------------
const vm = new Vue({
el: '#app',
render: c => c(App),
// 4 挂载到 vue 实例中
router
})
Mint-UI
// 1 导入 mint-ui模块
import MintUI from 'mint-ui'
// 2 导入 样式
import 'mint-ui/lib/style.css'
// 3 注册插件
Vue.use(MintUI)
MUI
// 只需要导入 MUI的样式 即可,根据MUI的例子,直接使用HTML结果即可
// 导入样式
import './lib/mui/css/mui.min.css'
ElementUI
{
"presets": [
["es2015", { "modules": false }], "stage-0"
],
"plugins": [
["component", [
{
"libraryName": "mint-ui",
"style": true
},
{
"libraryName": "element-ui",
"styleLibraryName": "theme-default"
}
]]
]
}
Webpack 发布项目
/* package.json */
"scripts": {
"build": "webpack --config webpack.prod.js"
}
1 在项目根目录中创建 webpack.prod.js 文件
2 在 package.json 中, 配置一个 scripts
3 在 终端中 通过 npm run build 对项目进行打包
1 删除掉 devServer 相关的配置项
2 将图片和字体文件输出到指定的文件夹中
3 自动删除dist目录
4 分离第三方包(将使用的vue等第三方包抽离到 vender.js 中)
5 压缩混淆JS 以及 指定生成环境
6 抽取和压缩CSS文件
7 压缩HTML页面
8 配合vue的异步组件,实现按需加载功能
/* webpack.prod.js */
// 处理URL路径的loader
{
test: /\.(jpg|png|gif|bmp|jpeg)$/,
use: {
loader: 'url-loader',
options: {
limit: 8192,
name: 'images/[hash:7].[ext]' // 作用:将图片输出到images文件夹中,文件名采用7位的哈希值(MD5),并且保持原来的图片文件扩展名
// name:指定文件输出路径和输出文件命令规则
// [hash:7]:表示使用7位哈希值代表文件名称
// [ext]:表示保持文件原有后缀名
// name: 'imgs/img-[hash:7].[ext]'
}
}
},
/* webpack.prod.js */
const cleanWebpackPlugin = require('clean-webpack-plugin')
plugins: [
// 创建一个删除文件夹的插件,删除dist目录
new cleanWebpackPlugin(['./dist'])
]
/* webpack.prod.js */
// 1 入口 -- 打包文件的入口
entry: {
// 项目代码入口
app: path.join(__dirname, './src/js/main.js'),
// 第三方包入口
vendor: ['vue', 'vue-router', 'axios']
},
output: {
// 2 修改输出文件路径和命名规则
filename: 'js/[name].[chunkhash].js',
},
plugins: [
// 3 抽离第三方包
new webpack.optimize.CommonsChunkPlugin({
// 将 entry 中指定的 ['vue', 'vue-router', 'axios'] 打包到名为 vendor 的js文件中
// 第三方包入口名称,对应 entry 中的 vendor 属性
name: 'vendor',
}),
]
压缩混淆JS
plugins: [
// 优化代码
// https://github.com/webpack-contrib/uglifyjs-webpack-plugin/tree/v0.4.6
new webpack.optimize.UglifyJsPlugin({
// 压缩
compress: {
// 移除警告
warnings: false
}
}),
// 指定环境为生产环境:vue会根据这一项启用压缩后的vue文件
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
})
]
抽取和压缩CSS文件
压缩和抽离CSS报错的说明:
Error processing file: css/style.css
postcss-svgo: Error in parsing SVG: Unquoted attribute value
原因:压缩和抽离CSS的插件中只允许 SVG 使用双引号
/* webpack.prod.js */
// 分离 css 到独立的文件中
const ExtractTextPlugin = require("extract-text-webpack-plugin");
// 压缩 css 资源文件
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
// bug描述: 生成后面的css文件中图片路径错误,打开页面找不到图片
// 解决:google搜索 webpack css loader 样式图片路径
output: {
// ...
// https://doc.webpack-china.org/configuration/output/#output-publicpath
// 设置公共路径
publicPath: '/',
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ['css-loader', 'sass-loader']
})
},
]
},
plugins: [
// 通过插件抽离 css (参数)
new ExtractTextPlugin("css/style.css"),
// 抽离css 的辅助压缩插件
new OptimizeCssAssetsPlugin()
]
new htmlWebpackPlugin({
// 模板页面
template: path.join(__dirname, './index.html'),
// 压缩HTML
minify: {
// 移除空白
collapseWhitespace: true,
// 移除注释
removeComments: true,
// 移除属性中的双引号
removeAttributeQuotes: true
}
}),
vue配合webpack实现路由按需加载
// 方式一: require.ensure()
const NewsList = r => require.ensure([], () => r(require('../components/news/newslist.vue')), 'news')
// 方式二: import() -- 推荐
// 注意:/* webpackChunkName: "newsinfo" */ 是一个特殊的语法,表示生成js文件的名称
const NewsInfo = () => import(/* webpackChunkName: "newsinfo" */ '../components/news/newsinfo.vue')
output: {
// ------添加 chunkFilename, 指定输出js文件的名称------
chunkFilename: 'js/[name].[chunkhash].js',
},