ES6教程
使用与Debug的引用模式:
以上的模式只适用于学习不能用于开发
使用与线上部署的模式
let 用于申明局部变量 用法类似于var 但是let命令所在命令块才会有效,有暂时型的死区约束
<html lang="en">
<head>
<title>Opening Jupyter Notebooktitle>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
head>
<body>
<div id="Hello">
{{message}},<h1>{{name}}h1>
div>
<script type="text/javascript">
const myvue = new Vue({
el: "#Hello", // 用于挂载管理元素
// delimiters: ['{[', ']}'] // 默认是{{}} 但是这与Jinja渲染冲突 所以我们可以修改他
data: {
message: "你好",
name:"这里可以存放任意长度的数据"
}
});
script>
body>
html>
我们之前使用js jq 进行开发,往往都是获取DOM对象在进行数据的设置或获取,那种设计模式叫做命令式设计模式(链表式编程范式)而Vue的设计模式是申明式编程(有点像是面向过程与面向对象之间的关系)
我们可以动态的修改data里面的数据
....
let myvue = new Vue({
el: "#Hello", // 用于挂载管理元素
// delimiters: ['{[', ']}'] // 默认是{{}} 但是这与Jinja渲染冲突 所以我们可以修改他
data: {
message: "你好",
name:"这里可以存放任意长度的数据"
}
});
myvue.message("Hello"); // 修改message的数据
列表的显示可以使用 for 循环
<body>
<div id="test">
<h1>{{Title}}h1>
<ul>
<li v-for='item in Students'>这里是{{item}}li>
ul>
div>
body>
<script type="text/javascript">
const myVue = new Vue({
el: "#test",
data:{
Title:" 学生列表",
Students: ["李四","张三","刘二"]
}
})
script>
在没有导入 Vue 的监听之前:
DOCTYPE html>
<html lang="zh">
<head>
<script src="vue.js">script>
<title>计数器的实现title>
head>
<body>
<div id="count">当前计数:{{myNumber}}div>
<button type="button" v-on:click="myNumber++">+button>
<button type="button" onclick="myVue.myNumber--">-button>
body>
<script type="text/javascript">
let myVue = new Vue({
el: "#count",
data: {
myNumber: 0,
}
})
script>
html>
导入Vue的监听之后:
<!DOCTYPE html>
<html lang="zh">
<head>
<script src="vue.js"></script>
<title>计数器的实现</title>
</head>
<body>
<div id="count">
当前计数:{{myNumber}}
<button v-on:click="add">+</button>
<button v-on:click="sub">-</button>
</div>
</body>
<script type="text/javascript">
let myVue = new Vue({
el: "#count",
data: {
myNumber: 0,
},
methods:{
// 加函数
add: function(){
this.myNumber ++
},
// 减函数
sub: function(){
this.myNumber --
}
}
})
</script>
</html>
v-on:click
可以简写成@click
在MVVM的框架下视图和模型是不能直接通信的。它们通过ViewModel来通信,ViewModel通常要实现一个observer观察者,当数据发生变化,ViewModel能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM中的View 和 ViewModel可以互相通信。
我们的计数器中就是一个严格的MVVM模型
创建Vue的时候我们可以创建以下几种对象:
el String | Object
data Object |Function
methods key:Funtion
如果有一处的标签我们只希望他显示一次性的数据,之后变量如何进行改变都不会波及到他,可以使用v-once
指令来规定
默认的,我们给一个data对象的变量设置html时 浏览器不会解析,我们需要给对应的DOM加入一个指令 v-html
,这个指令的值就是要解析的HTML,同时拥有该指令的DOM内部的文本不会被显示,同样的还有v-text
指令显示完全的文本,v-pre
表示原封不动的显示DOM的内部文本而不会被解析
{{}}中不仅可以添加变量还可以添加有关变量的表达式
DOCTYPE html>
<html lang="zh">
<head>
<script src="vue.js">script>
<title>胡子语法title>
head>
<body>
<div id="e">
可以进行+拼接: {{welcome + ' ' + username}}
<br>
或者使用HTML拼接: {{welcome}} {{username}}
<div v-once>
不会改变得到welcome : {{welcome}}
div>
<div v-html='bold'>
我不会被显示,因为这个div是负责解析bold属性的
div>
div>
body>
<script type="text/javascript">
let myVue = new Vue({
el: "#e",
data:{
welcome : "Hello",
username : "李狗蛋",
bold:"加粗字体"
}
});
script>
html>
data的属性使用{{}}也就是Mustache语法可以绑定到文本,使用v-bind可以将属性绑定到属性
HTML:
JS:
data:{picURL:"xxxxx.png"}
其中v-bind
可以进行简写成 ->
<h2 :class="{类名A:,类名2:flase}">h2>
如果类名后的boolean为true就将值给class,如果是false就不赋值。
我们可以将布尔值交给data,使用的时候直接写名称 不需要{{}} 这样我们就可以动态的设置类属性了
我们也可以利用数组动态绑定Class 但是用处较少 下来自行了解
使用对象可以动态绑定样式
:style = "{
FontSize:"20px" //样式的值如果是具体的就需要引号
}"
<h1> {{fullname}} </h1>
....
computed:{ // 表示计算属性
fullname : function(){
return this.fristName + this.lastName;
}
}
可以同时设置getter与setter
computed: {
fullname {
get(){
....
},
set(val){
....
}
}
}
DOCTYPE html>
<html lang="zh">
<head>
<script src="vue.js">script>
<title>点击列表标红title>
head>
<style type="text/css">
.Red{
color: red;
}
style>
<body>
<ul id ="myListGroup">
<li v-for="(item,index) in studentGroup" @click="setColorRed(index)" :class=
"{Red:selectedIndex == index}">{{item}}li>
ul>
body>
<script type="text/javascript">
let myVue = new Vue({
el: "#myListGroup",
data:{
studentGroup: ["李三","张思","王五"],
selectedIndex:0
},
methods:{
setColorRed(index){
this.selectedIndex = index;
}
}
});
script>
html>
v-on:事件
可以进行事件的监听, 可以简写成 @+事件名
.
{{val}}
当方法不需要参数的时候在绑定的时候可以不加入()
undefined
如果需要传入某个参数还需要传入event事件参数的时候可以通过 $event
传入事件
因为 事件冒泡
的机制上述的代码 按钮点击后会执行两个函数的内容, 如果想要按钮只执行按钮的方法 可以使用事件冒泡的机制进行实现,在vue
中可以直接使用 v-on的修饰符
即可.
我们只想让 button
执行 @click="a_cli"
的代码 可以使用 @click.stop='a_cli'
意味事件完.stop
的时候进行事件停止
阻止默认事件, 比如form
的submit
的自动提交事件 可以使用 prevent
进行事件的阻止,在进行一些处理之后再进行提交
监听键盘的输入 如 @keyup.enter
只监听回车按键事件
只触发一次的事件
v-if
的值为true
时显示数据 v-else
是v-if
为false
时显示 但是条件过多的时候建议使用计算属性 getter setter
v-show 是一定会被渲染的 当 v-show
为 false 的时候只是 display: none
但是 v-if
为 false 的时候 页面甚至不会进行渲染
开发的选择
当切换频繁的时候 为了让页面进行少量渲染 建议使用 v-show
当只有一次切换的时候建议使用 v-if
v-show吃内存,v-if吃CPU
DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="./vue.js" type="text/javascript" charset="utf-8">script>
<title>v-on的基本使用title>
head>
<body>
<div id='app'>
<h1>{{title}}h1>
<form v-if='showLogin' action="/login">
<input type="text" name="" placeholder="登录">
<input type="submit" @click.prevent @click="reg1" id="" name="" />
form>
<form v-else>
<input type="text" name="" placeholder="注册">
<input type="submit" @click.prevent @click="login1" id="" name="" />
form>
div>
<script>
let app = new Vue({
el: '#app',
data: {
title: "登录",
login: "/login",
reg:"/reg"
},
computed:{
showLogin:function(){
return this.title == "登录";
}
},
methods: {
reg1(){
this.title = "注册"
},
login1(){
this.title = "登录"
}
}
});
script>
body>
html>
如果我们把form的active删除掉 使得两个条件分支的form的属性只有一个v-if 或者是 v-else, 那么就会出现一个问题, 两个form表单的input输入数据在进行切换后 里面的数据不会被重置, 这是因为一个叫做 虚拟dom - vdom
的机制影响的
v-dom
将需要显示的 dom 加载到 vdom的内存中,然后在渲染到页面, 考虑其性能, 虚拟dom在进行渲染的时候会使用已经存在的元素,而不是重新创建元素
解决方法
给ele键入一个唯一的标志, 在我们的代码中 action就是一个标识,如果你是用的是其他的盒子,可以使用 :key
属性来进行标识
<li v-for = "i in array">
{{ i }} 普通数组的遍历
{{ i }} 如果array是对象的话 i 就是对象的 value
li>
<li v-for = "(val, index) in array">
{{ i }} 普通数组的遍历 第{{index+1}}个值
li>
<li v-for = "( alue, key, index) in array">
对象的key 与 value 相对于其他的语言是反着的 index还是在最后
li>
官方推荐: 推荐给 v-for 的元素或者组件加入 :key 属性
我们知道 push
方法是一个不错的响应式方法数组在push
发生修改后 vue会实时的修改这个变量绑定的元素, 但是并不是所有的方法可以做到响应式
支持响应式的是: push pop shift shift unshift splice
但是 索引修改数组值是不支持响应式的, 我们可以使用vue.set
来进行替换这个方案
vue.set(this.array, 0, "123"); // 修改 array 第0个元素 为 123
this.array.splice(0, 1, 'bbbbb'); // 原生JS也可以使用splice的替换进行修改
请完成一个项目 https://www.bilibili.com/video/BV15741177Eh?p=41&spm_id_from=pageDriver==[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Iy4QlPu-1645785342403)(C:\Users\Administrator.KON-20200831MDP\AppData\Roaming\Typora\typora-user-images\image-20211203094934868.png)]==
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
var ages = [32, 33, 16, 40];
function checkAdult(age) {
return age >= 18;
}
function myFunction() {
document.getElementById("demo").innerHTML = ages.filter(checkAdult);
}
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
var numbers = [4, 9, 16, 25];
function myFunction() {
x = document.getElementById("demo")
x.innerHTML = numbers.map(Math.sqrt);
}
汇总
var numbers = [15.5, 2.3, 1.1, 4.7];
function getSum(total, num) {
return total + Math.round(num);
}
function myFunction(item) {
document.getElementById("demo").innerHTML = numbers.reduce(getSum, 0);
}
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
reduce() 可以作为一个高阶函数,用于函数的 compose。
注意: reduce() 对于空数组是不会执行回调函数的。
那 Python 举例:
import functools;functools.reduce(lambda x,y: x+y, map(lambda x:x[1]+20,filter(lambda x:x[1] > 40, bookName_Money.items())))
v-model
作用 表单双向绑定
<input type="text" v-model="title"/>
这样我们 就让这个表单进行了双向的绑定 input的双向绑定 v-modle在对表单元素进行绑定的时候会直接把绑定的值给value
当value发生改变的时候 如用户进行的修改
变量值也会被改变 这就是 双向绑定
<input type="radio" value="man" id='man' v-model="sex"><label for='man'>男label><br>
<input type="radio" value='woman' id='woman' v-model="sex" ><label for="woman">女label><br>
这样不需要name就可以使得两个radio进行互斥
checkbox 元素一般会同意选择多个数据 所以 复选框的v-model形式按数组存贮 所以我们绑定给 checkbox
的变量必须是数组
<input type="checkbox" id='hobby1'value="羽毛球" v-model="hobby"> <label for='hobby1'>羽毛球label><br />
<input type="checkbox" id='hobby2'value="网球" v-model="hobby"> <label for='hobby2'>网球label><br />
<input type="checkbox" id='hobby3'value="足球" v-model="hobby"> <label for='hobby3'>足球label><br />
<h1>
当前hobby:{{ hobby }}
h1>
....
data{
hobby : [ ] // JS 代码
}
修饰符z | 作用 |
---|---|
lazy | 用户输入完毕才会进行修改 而不是实时的修改 |
number | 限制变量为number |
trim | 剥除两边的空格 |
extend
这种方式创建组件DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="./vue.js" type="text/javascript" charset="utf-8">script>
<title>组件的基本使用title>
head>
<body>
<div id="app">
<my-cpn>
my-cpn>
div>
<script>
const com = Vue.extend({
template: `
这是一个组件模板
你好
`
}); // 创建一个组件
const com2 = Vue.extend({
template: `
这是第二个组件模板
再见
`
}); // 创建一个组件
Vue.component("my-cpn", com); // 将组件注册成my-cpn 组件的标签最好不要大写
let app = new Vue({
el : "app"
})
script>
body>
html>
全局组件的注册使用component
进行,局部的组件式找到对应局域的Vue构造函数
let app = new Vue({
el: "#app"
comments: {
cpn: com; // 注册局部的组件
}
})
全局组件可以在任意的Vue管理范围区域使用 而局部组件只能在对应的管理区域进行使用
<body>
<div id="app">
<mycpn2>
mycpn2>
div>
<script>
const com = Vue.extend({
template: `
这是一个组件模板
你好
`
});
const com2 = Vue.extend({
template: `
这是第二个组件模板
我们可以在组件里面使用组件
`,
components:{
// 在extends构造器里面可以存在一个components
cpn1 : com // 为com2组件注册一个com的组件
}
}); // 创建第二个组件
let app = new Vue({
el : "#app",
components:{
mycpn2: com2
}
})
script>
body>
如上代码 com2
是 com
的父组件 注意 局部的组件必须在全局被注册或者是在局部生效
语法糖
我们很少使用extend创建组件,一般使用语法糖更简单
Vue.component("mycpn2",{
template: `
这是一个组件模板
你好
`
});
let app = new Vue({
el : "#app",
})
我们将传入extend
的参数对象放在 component
参数即可实现 构造器的使用与组件注册两个步骤,当然我们也可以通过这个形式创建局部语法糖组件
let app = new Vue({
el : "#app",
comments:{
"app": {
template: "局部组件"
}
}
})
抽离组件的写法
虽然使用了语法糖 但是代码看起来还是很乱的说 所以我们可以template的html单独抽离出来进行编写
- 创建一个script标签
- 他的
type
设置成 text/x-template
- 在里面写模板的文件
- 给这个标签设置id
- 给传入
extend
构造器 的对象的 template
对象赋值对应的 id
,记得加上#
当然 script
这个方式很少使用 我们可以将其设置成 template
标签然后为其设置id
即可
<div id="app">
<mycpn2>
mycpn2>
div>
<template id='myhome'>
<div>
<h2>这是一个组件模板h2>
<a href='http://www.baidu.com'>你好a>
div>
template>
<script>
Vue.component("mycpn2",{
template: "#myhome"
});
let app = new Vue({
el : "#app"
})
script>
组件使用变量
问题: 组件的内部 是不能访问 Vue的实例数据的
在上述的代码中 template的文本是写死的 我们给vue的date写入变量给template依旧无法显示,因为 组件的内部 是不能访问 Vue的实例数据的
解决方法
我们在注册, 也就是 compontent 方法里
,给传入的对象加入一个新的方法: data
,其必须是一个 函数
<div id="app">
<mycpn2>
mycpn2>
div>
<template id='myhome'>
<div>
<h2>这是一个组件模板:{{title}}h2>
<a href='http://www.baidu.com'>你好a>
div>
template>
<script>
Vue.component("mycpn2",{
template: `#myhome`,
data(){
return {
title: "abc"
}
}
});
let app = new Vue({
el : "#app"
})
script>
同样的 组件可以拥有 methods 等所有该用有的 是一个封闭的小空间
为什么是函数
如果创建了多个组件 防止多个组件在共享同一个data属性所作的隔离
组件之间的通信
网页请求服务器的时候 一般是最外部的组件进行请求,从而获取响应值,如何将外部组件的数据交给内部组件进行数据的展示, 是本章的主要内容
vue交给我们两种父子组件的通讯方式
父传子
<body>
<template id='child'>
<div>
This is Child: {{ name }} <br>
He live in {{ home }}
div>
template>
<div id="app">
<child :home="type">child>
div>
<script>
Vue.component("child", {
template: "#child",
props: ["home"] ,// 定义父传子的变量
data() {
return {
name: "LeLe"
}
}
})
let app = new Vue({
el: "#app",
data:{
type: "别墅"
// 因为使用了 :home="type" 所以这里可以使用 type:"别墅" 从而进行父传子
}
})
script>
body>
可以让 props 成为 对象 这样可以限制类型
props: {
movies: {
type: String, // 类型
default: "xxxx", // 默认值
required: true, // 这个变量是必须传入的值 v-bind
}
}
在vue2.5之后 当type为Array的时候 default必须是一个函数
props: {
movies: {
type: Array, // 类型
default(){
return [];
} , // 默认值
required: true, // 这个变量是必须传入的值 v-bind
}
}
除此之外我们可以自定义验证器
变量名称最好不要驼峰命名
子传父
常见的子传父使用的是事件, 子传父主要是用于子组件发生了一些事件需要父组件知道。
- 给子组件定义一个func 在 methods 这个方法指定了向 父组件 发送的事件(记得事件全小写)
- 父组件在使用的时候进行事件的绑定
- 绑定的事件可以对应父组件自己的函数事件
- 即可
在第二步的是时候 如果emit有向服务器发送对应的参数,那么第二步的时候会自动把参数交给函数执行,这样我们就不需要自己写实参了
children 与 refs
如果你想使用父组件拿到子组件的对象进行调用子组件对象的方法,或者反其道让子组件进行父组件的方法调用,这一章就对你大有裨益
- 父组件访问子组件 使用
$children
或 $refs
- 子组件访问父组件 使用
$parent
<body>
<template id='children-element'>
<div>
孩子
div>
template>
<div id="app">
<cpn>cpn>
<cpn ref="i-have-ref">cpn>
<button @click="father_click">父亲按钮button>
div>
body>
<script type="text/javascript">
let app = new Vue({
el: "#app",
components:{
cpn : {
template:"#children-element",
methods:{
sayHello(){
console.log("孩子在说话");
},
sayHello2(){
console.log("特殊的孩子在说话");
}
}
}
},
data: {
mes: "你好"
},
methods:{
father_click(){
console.log("父亲按钮被点击");
// 1、 $children 拿取
console.log(this.$children);
// 返回了一个数组里面存放若干个子组件
let child_ele = this.$children[0];
// 先拿一个子组件
child_ele.sayHello();
// 调用子组件的方法
// 2、refs 拿取 子组件中 带有 ref 属性的标签
let array_list = this.$refs;
let child_ele_2 = this.$refs['i-have-ref'];
child_ele_2.sayHello2();
}
}
})
script>
parent 与 root
与children refs类似 实际开发很少使用 故而不在赘述
插槽 slot
什么是插槽
插槽相当于组件的装饰,可以为组件内部的元素进行扩展
什么时候使用
基本使用
<html>
<head>
<meta charset="utf-8">
<title>title>
<script src="./vue.js" type="text/javascript" charset="utf-8">script>
head>
<body>
<div id="app">
<cpn>cpn>
<cpn><h1>你好h1>cpn>
div>
<template id='model'>
<div style="border: 1px black solid;margin-top: 20px;">
这是一个插槽,你需要放一个元素给她:<slot><h2>这是我的默认值h2>slot>
div>
template>
body>
<script type="text/javascript">
let app = new Vue({
el: "#app",
components:{
cpn:{
template:"#model"
}
}
})
script>
html>
具名插槽
一个模板可以拥有多个插槽为了区别各个插槽 我们可以给插槽一个名字 拥有名称的插槽就是 具名插槽
<html>
<head>
<meta charset="utf-8">
<title>title>
<script src="./vue.js" type="text/javascript" charset="utf-8">script>
head>
<body>
<div id="app">
<cpn>标题cpn>
<cpn >
<input slot="center" type="text">
cpn>
div>
<template id='model'>
<div style="border: 1px black solid;margin-top: 20px;">
<slot name='left'>左slot>
<slot name='center'>中slot>
<slot name='right'>右slot>
<slot>凑数的slot>
<slot>凑数的slot>
<slot>凑数的slot>
<slot>凑数的slot>
div>
template>
body>
<script type="text/javascript">
let app = new Vue({
el: "#app",
components:{
cpn:{
template:"#model"
}
}
})
script>
html>
作用域插槽
编译作用域
如下代码中
<div id='app'>
<cpn v-for="i in list">cpn>
div>
我们在子组件中使用了list, 那这个list是 父组件的还是子组件的? 答案: 父组件的
如下代码中
<template>
<div>
<li v-for="i in list">li>
div>
template>
我们在模板中使用了list, 那这个list是 父组件的还是子组件的? 答案: 子组件的
作用域插槽
作用:父组件替换插槽的标签 内容是子组件提供 也就是在确保数据依旧是子组件提供的情况下修改子组件插槽部分的数据···
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>作用域插槽 title>
<script src="./vue.js" type="text/javascript" charset="utf-8">script>
head>
<body>
<div id="app">
<cpn>
<template slot-scope='slot'>
<span v-for='i in slot.val_list_data'> {{i}} span>
template>
cpn>
div>
<template id='child'>
<div>
hello
<slot :val_list_data="list_data">
<li v-for="i in list_data">{{i}}li>
slot>
div>
template>
<script type="text/javascript">
let app = new Vue({
el: "#app",
components: {
cpn: {
template: "#child",
data() {
return {
"list_data": ["第一步", "第二步", "第三步"]
};
}
}
}
});
script>
body>
html>
webpack
安装
需要先安装node,js
, 并使用 npm
管理 webpack
.
node.js
的安装请查看 AJAX
npm
安装 webpack
命令
npm install [email protected] -g
-g
表示全局安装, 有两种安装方法:
- 全局安装
- 局部安装
使用 webpack -v
命令进行webpack
是否安装完毕 的检验
使用
webpack的项目中一般存在两个目录:
当然还需要存在一个index.html的玩意~
- 在 src 中创建一个 模块 我写的是
math_util
是一个数据求值库
- 将
math_util
一些重要的函数或者变量导出(commandJS 方法 或者是 CMD AMD 方法都可以)
- 在
main,js
导入数据 并执行导入的方法
代码
这是项目结构:
-- math_util.js
function add(number1 , number2) {
return number1 + number2;
}
module.exports = [add]
// 使用CommandJs进行导出
-- main.js:
const add = require('./math_util.js')[0]
console.log(add(1,2))
现在我们不能在 HTML
代码中写入 main.js
的 引用 因为 commandJS
不能是 JS
原生的支持 , 我们需要 webpack
进行 源码的打包
在项目根目录中执行cmd指令
webpack ./src/main.js ./dist/bundle.js
- 我们只需打包main.js吗?
- 在我们的代码中我们的
main.js
导入了一些依赖库(math_util.js
) webpack
会自动打包这些依赖
- 如果 webpack 版本在 4.0 以上 使用代码: npx webpack .\src\main.js ./dist/bundle.js
- 这个只是听说不知道可以使用否
完成了这些操作我们只需要设置boundle.js
的引用 —— 在html
代码中就是了
webpack 的配置
webpack ./src/main.js ./dist/bundle.js
这段命令是在是太长了 每次都要敲 麻烦! 我们可以给 webpack 进行设置,这样输入webpack
就可以了 本章教你如何实现这样的功能
首先在项目根目录定义一个文件 这个文件的文件名称 固定为 webpack.config.js
因为我们要在webpack.config,js
中使用指定的第三方模块, 所以在正式开发之前我们需要执行命令(需要在项目根目录里)
npm init
npm install
完成之后就有了一个package.json
文件出现
开始编写 webpack.config,js
的内容
const path = require('path'); // 导入 node 的 JS
module.exports = {
entry: __dirname + "/src/main.js", // 源文件 必须是绝对路径
output: {
path: __dirname + '/dist'
// 打包后的目录 必须是绝对路径
, filename: 'bundle.js' // 打包后的文件名
}
}
完成这几步之后在使用命令webpack即可实现打包
但是在实际开发中 我们不常使用 webpack
进行打包,而是使用 npm run build
指令
只需要修改 package.json
文件即可进行修改
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
开发时依赖环境安装
我们在使用webpack 可能会有多个项目同时开发 A项目是 3.5版本的WP B项目事是5.1版本的WP,为了让两个项目可以顺利开发,我们会给每个项目安装一个 开发时依赖
.
确保存在一个package.json
在项目根目录中使用:
npm install [email protected] --save-dev
但是在 CMD 执行的任何webpack都是全局的,为了让这个依赖环境有效, 我们需要模仿上一章 使用 npm run build
指令进行打包
CSS | 资源文件的配置
webpack
不支持css等非JS的文件的转化,我们需要给webpack
做一个升级, 也就是使用 npm
安装loader
css
- 创建
css
文件
- 在
main.js
中导入这个 css
require("./cee/normal.css")
完成准备工作后我们就去官网翻牌我们需要的loader
: https://www.webpackjs.com/loaders/
安装我们的 loder
如果webpack的版本是3.6.0
安装css-loader的时候记得安装 2.0.2 版本的 否则会出错 [email protected]
-
使用的是开发时依赖:
npm install --save-dev css-loader
npm install --save-dev style-loader
-
在导出配置 也就是webpack.config.js
中加入:
module: {
rules:{
test: /.css$/,
use: [‘style-loader’, css-loader’]
}
}
style-loader 负责样式添加到css 中
css-loader 是负责css的文件加载
web的加载 方式是从右到左 所以我们要先加载css 在加载style 所以需要: [‘style-loader’, css-loader’]这么定义
-
打包
安装成功 !
Less 文件的配置
1、 先写入一个less文件 less文件的写法是这样的:
@fontSize: 30px;
@ColorFont: red;
body{
font-size: @fontSize;
color: @ColorFont;
}
2、 然后还是老样子在 JS 导入依赖
3、 为了让我们的 webpack
有读取 less
文件的能力 需要安装 less
npm install less
4、 安装完毕后,安装对应的less-loader文件即可
版本如上图
在 rules
中 添加规则
:
test:/\.less$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader"
},
{
loader: "less-loader"
}
]
图片的配置
1、现在我们的css
或者less
中写入 图片文件的使用即可
2、 然后找到对应的加载器,这次的加载器不在是加载 样式而是文件: url-loader
npm install --save-dev [email protected]
3、 设置配置
{
test: /\.(png|jpg|gif|jpeg)/,
use: {
loader:'url-loader',
options:{
limit: 27000 // 限制大小
}
}
}
如果我们有的图片大于了options:limit的大小 在进行打包的时候就会报错 :-
为了加载比limit大的图片我们可以先下载一个[email protected]
, 但是 依旧无法显示,我们只需要在导出文件的output
属性中加入 publicPath:'dist/'
即可 这表示 只要设置url这样的属性的时候会自动进行拼接publicPath
。
图片文件名称的修改
打包后的图片都会存放在dist的根目录下,他是图片名称转化成的hash值,非常难以看懂,所以我们需要将其转化为我们看得懂的名称(也就是原名称),为了文件名重复,我们可以在原名称后面加上hash值, 但是hash值太长 我们可以做一个截取 取8位就是了,然后在dist中我们需要图片在img
文件夹下,all in all, 我们可以在 打包配置文件(webpack.conf.js) 的 options
写入代码:
limit:111111,
name: 'img/[name].[hash:8].[ext]'
这就是操作的效果图
ES6 转 ES5
有些浏览器是不支持Es6的为了适用性扩大,我们一般使用es5
, 使用babel
工具进行转换
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
由于IE下架本章不在阐述…
脚手架 4.x
vue-cli4.x开发 + Vue2.x开发
什么是脚手架?
如果你只是简单的写几个Vue的Demo程序 那么你不需要VueCli,但是如果你在开发大型香蜜, VueCLI是你的不二之选
再不使用cli的时候, Vue.js开发大型应用 我们需要考虑代码目录结构 项目结构和部署热加载与单元测试等事情
使用vue-cli可以快速搭建Vue的开发环境
安装Vue我们可以直接使用 全局安装 也就是 npm install -g @vue/cli
我们可以使用 命令vue --version可以获取脚手架的版本.
项目的简单创建
-
先安装一个全局的插件: npm i -g @vue/cli-service-global
-
在创建一个 app.vue
并写入如下的代码:
我是谁?
刘博源
我们也可以使用vue build命令进行打包的操作
默认启动的文件是App.vue 或者是 app.vue 如果需要启动非默认的文件可以进行指定操作
vue serve -c index.vue
项目的创建
我们可以使用 vue create 项目名称
创建VueCli项目,当然你要选择Vue3版本 输入命令之后稍等片刻进行安装…
然后通过安装后的命令提示启动服务
cd xxx
npm run serve
接着使用 localhost:8080
进入网页
项目中存在一个src目录是我们的开发目录,默认存在的有:
- main.js
- App.vue
- assets - 多媒体文件目录
- components - 主要的组件
使用 npm run build
进行打包 (需要在项目中运行该命令).
图形化操作
使用 vue ui 打开GUI 操作模式, 这个也可以创建一个新的项目
组件化开发
组件化开发的模式其实就是.vue
文件的使用, 我们知道 .vue
文件是 html + js + css 三个主要标签实现的, 他们分离了主键的关联,提高了内聚, 开发完毕后我们可以打包好文件让浏览器可以正常识别.
main.js解析
import Vue from 'vue'
// 引入 Vue.js 框架
import App from './App.vue'
// 引入 App.vue 组件
Vue.config.productionTip = false
// 去除警告
// 创建根实例 ( Vue 全局实例)
new Vue({
render: h => h(App), // 渲染视图主键并且注册App.vue
}).$mount('#app') // 将APP组件的信息挂载到#app中
整个页面的html结构是public/index.html 中默认提供的模板 我们是用#app挂载的App.vue就会自动的修改,
创建一个LBY.Vue
在components
文件夹中.
我只是测试
然后对 App.vue
进行修改
Router
作用是创建单页面的项目
创建项目
- 取消 Linter / Formatter
- 勾选 Router
- 回车选择2.x
- 是否选择history模式? no,区别 http://localhost:8080/#/about 与 http://localhost:8080/about 的区别
- In package.json
等待
如何启动 history 模式?
我们在创建的时候取消了history模式而是进入了Hash模式取消Hash模式的方法就是在router/index.js中修改:
const router = new VueRouter({
mode: ‘history’,
routes
})
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router' // 相对传统的创建多了一个router的引用
Vue.config.productionTip = false
new Vue({
router, // router的注册 , 注册之后vue组件才可以使用router 相关的标签
render: h => h(App)
}).$mount('#app')
App.vue
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/', // 路由
name: 'Home', // 别名
component: Home // 加载的组件 渲染到 router-view
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') // 懒加载
}
]
const router = new VueRouter({
// mode: 'history',
routes
})
export default router
小例子 加入一个List页面
- 创建Vue
测试用例
- 在routers中注册vue
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import List from "../views/List.vue"
Vue.use(VueRouter)
const routes = [{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import( /* webpackChunkName: "about" */ '../views/About.vue')
},
{
path: '/list',
name: 'list',
component: List
}
]
const router = new VueRouter({
// mode: 'history',
routes
})
export default router
- App.vue中创建
动态路由匹配
什么是动态路由匹配?
将不确定的参数进行路由映射到同一个组件上去 比如经典的user?id=5
,其中5就是动态的数值 最终路径就是,他会成为 /user/5 其中 $route与params
Cli会组合成一个对象 如{ id : 5 }
创建一个类似的组件
这是我的组件
该组件名称: {{$route.name}}
该组件的id参数: {{$route.query.id}}
$route.name
是获取route的组件名称, route.query
可以获取url的参数对象
如果是 /xxx/id 的传输方式的话就是动态的获取参数对象 使用 route.params
即可
param值的获取: {{$route.params.id}}
注册的时候也需要注意
{
path: "/p_test/:id",
name: "p_test",
component: p_test
}
冒号加参数名称可以实现动态路由的匹配
路由捕获
{
path: "*",
name: '404',
component: LostPage
}
注册这样的路由在 index.js 中可以获取 非法url的请求
path: "user-*"
以 user-开头的路由都向这里走.
嵌套路由
在某个实际业务中 我们要实现一个游戏卡池系统,这个卡池系统有两个子系统: 公开招募系统
与 特殊招募系统
。两个系统需要一个动态参数用来获取卡池的版本号信息, 当然在一个系统中包括多个子系统就可以使用我们的嵌套路由来实现.
- 创建一个父组件
这是默认页显示的数据...
- 创建子路由的组件
这是一个版本为{{$route.params.version}}的特殊招募系统
- public与上述的类似
- 注册组件
{
path: "/recruit/:version", // 大局系统
name: "recruit",
component: default_recruit,
children:[
{
path: "public",
component: public_system
},
{
path: "private",
component: private_system
}
]
}
···
导航式编程
- 是组件标签可以直接进行导航,但缺乏编程性,无法进行各种逻辑判断
- 插件是提供了编程式的导航 让我们自行定义我们的导航方法,
....
<button @click="golist" >点击我</button>
...
<script>
export default{
methods:{
golist() {
this.$router.push("/about");
}
}
}
</script>
出现Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation错误是同一页面不能出现两个相同的路由导航
this.$router.push
可以向路由后加入新文本从而进行访问
当然你可以使用键值对进行访问
golist() {
this.$router.push(
{
path: '/test'
});
}
这种方法一般可以适用在带参数 url 中
<script>
export default {
methods: {
golist() {
this.$router.push({
name: 'p_test', // 此处使用的是组件的name属性
params: {
id: 4
} // 如果是query传参就使用query
});
}
}
}
script>
使用 $router.go(-1) 回到历史页
使用 $router.go(1) 前往下一页
命名路由与命名视图
命名路由
在上一章我们知道name
可以 与params
或者是 query
进行配合从而进入路由的导航,组件也可以这么使用
<router-link :to="{name: 'p_test', params:{id:125}}">TestByParam(默认125)router-link> |
这就是路由命名的简单的使用,同时我们可以命名视图
命名视图
我们知道, 是视图标签 组件通过这个标签来渲染组件的内容
- 创建一个组件
- 在
index.js
中进行注册
- 在
App.Vue
中定义组件的插槽()
我们可以使用Home组件来查看作用
创建一个组件
这是一个固定的组件
将组件创建在
{
path: '/',
name: 'Home',
components: {
default: Home, // 默认的组件显示
top: Top // name为top的router-view渲染Top的组件
}
}
路由别名
{
path: "/list",
name: "list",
alias: "/mylist"
}
别名可以为path设置多路由
重定向
vue-cli
也可以实现重定向的操作
{
path: "/user",
redirect: "/UserInfo"
}
这就是访问/user的时候跳转到UserInfo中
或者是对象传参
{
path: "/user",
redirect: {
name: "UserInfo"
}
}
使用箭头函数可以进行闭包的操作
{
path: "/user",
redirect: ()=>{
if (this.validate){
return "/UserInfo"
}else{
return "/index"
}
}
}
组件传参
如果我们希望User/5
中获取的id时, 不希望{{$router.params.id}}
获取, 而是{{id}}
这样直接获取
- 在组件创建的地方建立一个
props
属性 当然你可以给她默认值或者是类型
param值的获取: {{id}}
在 index.js
中创建组件注册的地方 将props
的值设置为true
.
Node.js 静态服务器的部署
Hash模式
- 先将服务器设置为 hash 模式
npm run build
# 打包
npm i serve -g
# 安装serve
serve dist
# 对 dist 目录下的 dist 进行挂载
History模式
在History模式中我们依旧可以向Hash模式一样进行挂载,但是,刷新等操作后会出现404的错误,为了防止这样的错误再次出现,我们可以使用命令: serve dist -s
导航守卫
全局前置
作用:
跳转的链接的前置与后置 可以进行跳转或者是取消跳转的操作
使用:
我们在 index.js
中加入一个路由守卫:
router.beforeEach(
(to, from, next)=>{
/**
to: 目标的路由对象
from: 正要离开的路由对象
next: 钩子函数
*/
}
)
注意不要将这段代码写在 export default router
之后
如果路由守卫什么都不做 那么网页无法实现正常的跳转 因为路由守卫有一定的拦截作用,为了取消这种拦截我们可以在回调函数里面使用next()
函数,next()
支持指定的资源, next(false)
可以取消导航跳转
避免以下的问题
如果我们要实现登录功能(登陆失败不跳转登陆成功才跳转)
const flag_login = false;
router.beforeEach(
(to, from, next)=>{
if (flag_login){
console.log(to)
next();
}else{
next("/shopping");
}
}
)
export default router
按理说我们点击登陆后会登陆失败从而进入shopping
页但是我们发现一旦登陆失败会出现无限递归的错误(RangeError
)
为了避免这样的错误我们可以在指定的页面才进行跳转
const flag_login = false;
router.beforeEach(
(to, from, next)=>{
if(to.name == "Login"){
if (flag_login){
console.log(to)
next("/user_index");
}else{
next("/");
}
}else next("/login")
}
)
export default router
全局后置
afterEach
就是后置函数 只有两个参数to
from
用于页面加载完成之后操作的一些动作
路由独享守卫
路由独享守卫 在某个路由内设置 仅仅供这个路由使用.
{
path: '/about',
name: 'About',
component: () => import( '../views/About.vue'), // 这是懒加载
beforeEnter: (to, from, next) => {
next("/shopping")
}
}
这样我们在/about
页前往其他页面的时候就会直接跳转在/shopping
组件守卫
组件守卫有三种状态 beforeRouteUpdate beforeRouteLeave beforeRouteEnter
分别是复用态 离开态 与 激活态,与其同行的还有 this
对象
- 复用态是指在
/:id
之类的地方 但是第一次是激活 后面才是复用 可以获取this
对象
- 激活就是加载后 由于组件被创建不久
this
对象是无法获取到的
- 离开就是回收后 可以获取
this
对象
组件守卫不用卸载 index.js
而是具体的组件.vue
的script
的export
部分
虽然我们难以在 激活态 也就是beforeRouteEnter
中获取this
但是我们还是可以使用next
传入回调进行获取:
...
beforeRouteEnter: (to, from, next)=>{
next(vm=>{
console.log(vm);
})
}
...
元信息
我们可以给路由设置一个mate
对象 在守卫中我们可以利用 to
获取Route
的mate
对象, 由于给路由设置的所以我们是在 index.js
中进行设置
{
path: '/index/home',
name: "index.home",
component: Route_home,
mate: {
title : 'mate的标题'
}
}
使用代码这么获取
BeforeEach: (to, from, next){
...
let title = to.mate.title;
...
}
过渡动效
作用就是专场效果.
路由数据获取
路由完成后获取
- 我们先创建一个vue组件 并设置两个 带
v-if
的盒子, 将 v-if
所使用的 成员
加载到 Loadding
, 默认值为 null
或者是 false
loading
post
- 注册组件在
index.js
中
- 这样我们的组件已经创建完毕了,现在我们只需要执行获取数据即可
loading...
title
{{post.title}}
author
{{post.who}}
路由完成前获取
组件守卫仿佛可以完成类似的事情 比如 beforeRouteEnter
自己完成哈, 就不写代码了
路由滚动
当显示的网页 height > 1vh 的时候 屏幕就需要使用滑块进行滑动 当我们的滑块位置不再 x:0 y:0的(也就是最初始的位置)前往其他的路由 浏览器依旧会保存滑块所在的位置(前提是 两个路由的vh或者是vw > 1), 我不想记录滑块的位置 如何完成
index.js
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
scrollBehavior(to, from, savedPosition) {
return {
x: 0,
y: 0
}; // 每次切换的时候回到自己的位置
}
})
return savedPosition
表示依旧保留切换前的位置
Vuex
什么是Vuex
-
Vuex 用于解决组件与组件之间共享的状态 (非父子组件的通讯)
-
使用devtools工具查看是否安装了Vuex
我们先写一个计数器
{{number}}
我们在 /Test 中注册这个组件发现虽然我们可以使用但是切换到其他路由之后再回到 /Test 这个状态是无法被保存的
Store 模式
如果是大型的单页面应用使用VueX会繁琐冗余 也就是说你的应用在及其简单的情况下使用Vuex是大材小用了,使用 store
模式可以帮助你在极少地共享数据
- 在项目中创建一个
src/store
目录 并在里面创建一个index.js
- 在
index.js
中加入如下代码
let store = {
// 共享数据
state: {
public_number: 0
},
// 累计数据
add(){
this.state.public_number ++;
}
}
export default store; // 导出 store
- 然后修改之前的计数器组件
{{public_State.public_number}}
Vuex 使用方式
安装Vuex
的时候选择Vuex
插件即可安装Vuex
发现 src/store
中加入了这么一段代码
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
}, // 状态值
mutations: {
}, // 修改状态的方法
actions: {
}, // 接口异步请求 服务器的数据
modules: {
}
})
我们使用Vuex来实现一个全局共享的计数器.
- 先再
store/index.js
中加入 状态
state: {
count: 10
}, // 状态值
- 我们就直接使用
About.vue
进行计数器的编写
This is an about page
{{$store.state.count}}
那我们可不可以直接进行修改呢? 虽然是可以,但是是不能保存状态的,所以直接修改一般而言是不能的(有点像 PySide 的多线程信号)
State 状态
Vuex
是通过 单例模式
创建的 store
实例 也就是说 一个Vue应用只能存在一个 store
.
我们使用计算属性进行之前的简洁写法
// store/index.js
...
state: {
count: 10,
age: 20,
name: "刘博源",
leavel: "A"
}, // 状态值
...
{{name}}
{{age}}
{{leavel}}
但是这样虽然在h1处体现出了简洁 但是 在计算属性的地方依旧是冗余代码过多,为了避免这样的问题我们可以使用 vuex的 mapState 辅助函数
{{name}}
{{age}}
{{leavel}}
但是这样的使用方式只能在 组件使用的变量名与在index.js中注册的状态变量名称一致的时候才可以使用 但是我们可以使用对象的模式进行显示
...
...
如此使用 其他的计算属性的话需要使用对象展开运算符...
export default {
name: "Student",
computed: {
...mapState({
a: "name",
b: 'age',
c: 'leavel'
}),
frist_name(state){
return state.name[0];
}
}
}
Getters 派生状态
有些时候我们需要对Store
的 状态 值进行简单的处理 比如说给某个变量的违规词进行删除,使用后MapStore
仿佛是个不错的选择但是很多组件都需要这个功能的时候,再去一个个的添加就很麻烦了, 这个时候我们就需要使用Getters
来实现了,
// 该代码放在 store/index.js 的 store 构造器的参数里
getters: {
getName(state){
return "[" + name + "]";
}
}
使用的时候记得不是调用函数而是使用属性即可
{{$store.getters.getName}}
同样的 Getters 也有对应的映射简写模式 mapGetters
Getters 的参数
Getters
支持参数的编写 但是Getters
本身使用的时候不是以函数调用 所以Getters
需要这么使用
getName(state){
return (id)=>{
return "[" + state.name + " " + id + "]";
}
}
使用
{{c}} {{$store.getters.getName(3)}}
Mutations 同步提交
我们可以在 index.js
中写入修改状态值的函数 ,然后在组件中使用mapMutaions
进行绑定
import {
mapState, mapMutations
} from "vuex"
export default {
name: "Student",
methods:{
...mapMutations(['ageValueAdd'])
},
computed: {
...mapState({
a: "name",
b: 'age',
c: 'leavel'
})
}
}
// index.js
export default new Vuex.Store({
state: {
...
}, // 状态值
getters: {
...
},
mutations: {
addvalue(state){
state.count ++;
},
ageValueAdd(state){
state.age += 1;
}
}, // 修改状态
actions: {
}, // 接口异步请求
modules: {
}
})
Actions 异步提交
Mutations
只能处理同步函数, 处理异步函数的时候需要使用Actions
函数, 因此 API
的数据就可以通过Actions
获取
name:{{$store.state.name}}
// index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
name: "需要获取信息",
age: 12
},
mutations: {
grow_up(state) {
state.age += 1;
},
getInfo_mutations(state, name) {
state.name = name
}
},
actions: {
getInfo_Actions(context) {
setTimeout(
() => {
context.commit("getInfo_mutations", "离散")
}, 1000
)
}
},
modules: {}
})
支持 mapActions
的使用
Module 的使用
作用
- 由于 state 使用的是单一状态树,所有的状态都需要几种在一个对象中 状态一多往往就会很麻烦
- 使用
Module
模块化设计可以将状态风格成一个个小模块.
状态的使用
使用
-
创建一个 store/module
的目录
-
在里面创建一个 Teacher.js
的文件 并 export default
导出 空对象
export default {
state:{
name: "pink老师"
},
getters: {
getNameTeacher(state){
return state.name + "老师";
}
}
}
- 在
store/index.js
中引用 Teacher.js
文件,
import Vue from 'vue'
import Vuex from 'vuex'
import Teacher from "./Module/Teacher.js"
Vue.use(Vuex)
export default new Vuex.Store({
state: {...},
...
modules: {
Teacher
}
})
- 创建
Teacher
组件 并注册到 router/index.js
中
老师: {{ $store.state.Teacher.name }}
学生: {{ $store.state.name }}
状态修改函数的调用
但是我们发送状态修改到mutations
就不用使用Teacher.commit
了 而是直接使用 commit
methods: {
getName(){
this.$store.commit("getTeacher")
}
}
也就是说我们可以直接使用 模块的修改状态函数
老师: {{ $store.state.Teacher.name }}
学生: {{ $store.state.name }}
但是当我们的模块里的修改状态函数与另一个模块的修改状态函数(包括 index.js
里的修改状态函数)重名的时候, 会全部执行, 为了单独使用某个模块的修改状态函数 我们可以直接给模块设置命名空间来进行区别
注册
export default {
namespaced: true, // 开启命名空间
state:{
...
},
getters: {
...
},
mutations: {
...
}
}
就这么使用即可:
this.$store.commit("Teacher/ChangeTeacher")
状态Getters的使用
return this.$store.getters["list/name"]
文件抽离
如果我们的store
对象或者是模块对象的actives
mutations
state
等对象代码过大的时候 我们可以单独的去创建一个文件进行分离
分离之后在 js 文件中import分离出去的代码即可
比如我将某 index.js 的 state进行分离
import state from "../State/Teacher"
export default {
namespaced: true, // 开启命名空间
state, // ES6 语法 ES5为 state: state
getters: {
getNameTeacher(state){
return state.name + "老师";
}
},
mutations: {
ChangeTeacher(state){
state.name = "罗翔";
}
}
}
这是我们分离出去的代码
export default {
name: "pink老师"
}