原创不易,转载请注明出处,谢谢!
第一部分:Vue基础语法——组件化开发——模块化开发——webpack
Day 01
01.(了解)vue.js课程介绍
- Vue.js课程学习路线
- Vue基础语法——组件化开发——Vue CLI——vue-router——vuex详解——网络封装——项目实战——项目部署——Vue.js原理相关
02.(理解)vue.js的特点和认识介绍
- Vue是一个渐进式框架,什么是渐进式呢?
渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来更丰富的交互体验。
或者如果你希望将更多的业务逻辑使用Vue实现,那么Vue的核心库以及其生态系统。
比如Core+Vue-router+Vuex,也可以满足你各种各样的需求。
03.(掌握)vue.js安装方式
-
方式一:直接CDN
-
方式二:下载和引入
开发环境 https://vuejs.org/js/vue.js 生产环境 https://vuejs.org/js/vue.min.js |
- 方式三:NPM安装
后续通过webpack和CLI的使用,我们使用该方式。
04.(掌握)HelloVuejs初体验
- 原生改变元素内容和vue改变元素内容区别
- el:挂载管理的元素
<body>
<!-- vue模式html -->
<div id="app">{{message}}</div>
<!-- 原生模式html -->
<div id="dom"></div>
<!-- vue改变元素内容,叫声明式编程 -->
<script src="./js/vue.js"></script>
<script>
//let变量、const常量
const app = new Vue({
el: '#app', //用于挂载要管理的元素
data: { //定义一些数据
message: '你好啊vue'
},
})
</script>
<!-- 原生改变元素内容,叫命令式编程-->
<script>
document.querySelector('#dom').innerHTML = '你好啊dom';
</script>
</body>
05.(掌握)Vue列表的显示
- 传统改变ul>li内容和vue的v-for遍历内容的区别
- 响应式:app.movies.push("龙猫"),页面直接就显示新增内容,而传统的dom操作需要创建一个li,然后再加内容
<body>
<!-- vue替代遍历列表内容 -->
<div id="app">
<ul>
<li v-for="item in movies">{{item}}</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '',
movies: ["功夫", "千与千寻", "肖申克救赎", "泰坦尼克号"]
}
})
</script>
<!-- 传统dom遍历列表内容 -->
<ul class="con">
<li>{{movies[0]}}</li>
<li>{{movies[1]}}</li>
<li>{{movies[2]}}</li>
<li>{{movies[3]}}</li>
</ul>
<script>
document.querySelector('.con').querySelectorAll('li')[0].innerHTML = "功夫"
document.querySelector('.con').querySelectorAll('li')[1].innerHTML = "千与千寻"
document.querySelector('.con').querySelectorAll('li')[2].innerHTML = "肖申克救赎"
document.querySelector('.con').querySelectorAll('li')[3].innerHTML = "泰坦尼克号"
</script>
</body>
06.(掌握)小案例-计数器
- 计数器代码:
<body>
<div id="app">
<h2>计数器:</h2>
<button @click="sub">-</button>
<span>{{count}}</span>
<button @click="add">+</button>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
count: 0,
},
methods: {
sub: function() {
this.count-- //this指向当前对象
},
add: function() {
this.count++
}
},
})
</script>
</body>
07.(理解)Vue的MVVM模式
- MVVM:MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。
08.理解Vue的options选项
- 创建new Vue({})的时候,就传入了一个options对象
- 需要掌握的选项(不止这些):
el 类型:string | HTMLElement(“|”是或的意思)
作用:决定之后Vue实例会管理哪一个DOM
data 类型:Object | Function
作用:Vue实例对应的数据对象
methods 类型:{[key:string]:Function}
作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。
- 需要掌握的选项(不止这些):
- 方法和函数的区别:
方法 | 函数 | |
英文单词 | method | function |
定义 | 方法(method)是通过对象调用的javascript函数。也就是说,方法也是函数,只是比较特殊的函数。 | 函数(function)是一段代码,需要通过名字来进行调用。它能将一些数据(函数的参数)传递进去进行处理,然后返回一些数据(函数的返回值),也可以不返回数据。 |
09.(理解)什么是Vue的生命周期
10.Vue的生命周期函数有哪些
- 生命周期:Vue实例有一个完整的生命周期,也就是说从开始创建、初始化数据、编译模板、挂在DOM、渲染-更新-渲染、卸载等一系列过程,我们成为Vue 实例的生命周期。
- Vue部分源码:(可以说明Vue在new Vue的时候做了很多事情)
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
Vue.prototype._init = function (options?: Object) {
......
}
- 下边created和mounted会在new Vue()的时候执行:
<script>
const app = new Vue({
el: '#app',
data: {},
methods: {},
created: function() {
console.log("created"); //控制台打印“create”
},
mounted: function() {
console.log("mounted"); 控制台打印“mounted”
},
})
</script>
- Vue生命周期图(重要★)
11.(了解)定义vue的template
- 自定义用户代码片段,vscode:文件-首选项-用户片段-html.json,加入以下代码:
"vue": {
"prefix": "vue", // 触发的关键字 输入vue按下tab键
"body": [
" <div id=\"app\"></div>",
" <script>",
" const app = new Vue({",
" el:'#app',",
" data:{},",
" methods:{}",
" });",
" </script>",
],
"description": "vue template"
}
12.(掌握)插值操作-mustache语法
-
{{message}}
<div id="app">
<h2>{{message}}</h2>
<h2>{{message}},龙猫</h2>
<!-- mustache语法中,也可以直接写简单的表达式 -->
<h2>{{firstName +' '+ lastName}}</h2>
<h2>{{firstName}}{{lastName}}</h2>
<h2>{{count*2}}</h2>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
firstName: 'mao',
lastName: 'long',
count: 100
},
methods: {}
});
</script>
13.(掌握)插值操作-其他指令的使用(v-once、v-html、v-text、pre)
- v-once 只渲染一次,不会随着数据改变而改变,
<h2 v-once>{{message}}</h2>
- v-html按照HTML格式进行解析,并且显示对应的内容
- v-text作用和Mustache一致,通常情况下,接受一个string类型
- v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法
- v-cloak避免在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签
<div id="app">
<!-- 显示'<a href="http://www.baidu.com">百度</a>'-->
{{url}}
<!-- 显示:百度-->
<div v-html="url"></div>
<!-- v-text和{{}}几乎一样,不会解析html元素,还不如{{}},因为会覆盖内容-->
<div v-text="url"></div>
<!-- v-pre不会解析{{}},原封不动显示,用的不多 -->
<div v-pre>{{message}}</div>
<!-- vue解析之前,div有一个熟悉叫v-cloak,解析之后,没了,[v-cloak]{display:none} -->
<div v-cloak>{{message}}</div>
</div>
<script src="./js/vue.js"></script>
<script>
setTimeout(() => {
const app = new Vue({
el: '#app',
data: {
message: '你好',
url: '<a href="http://www.baidu.com">百度</a>'
},
methods: {}
});
}, 1000);
</script>
14.(掌握)v-bind的基本使用
- v-bind用于绑定属性,如a元素的href属性,img元素的src属性
- v-bind的缩写是冒号:
<div id="app">
<img :src="imgUrl" alt="">
<img v-bind:src="imgUrl" alt="">
<a :href="baiduUrl">百度一下</a>C
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '类名',
imgUrl: 'https://cn.vuejs.org/images/logo.png',
baiduUrl: 'https://www.baidu.com',
},
methods: {}
});
</script>
15.(掌握)v-bind动态绑定class(对象语法)
- 对象语法的含义是:class后面跟的是一个对象
用法一:直接通过{}绑定一个类 Hello World |
用法二:也可以通过判断,传入多个值 Hello World |
用法三:和普通的类同时存在,并不冲突 注:如果isActive和isLine都为true,那么会有title/active/line三个类 Hello World |
用法四:如果过于复杂,可以放在一个methods或者computed中 注:classes是一个计算属性 Hello World |
<head>
<style>
.active {
color: red;
}
.line {
font-size: 60px;
}
</style>
</head>
<body>
<div id="app">
<div :class="{active:isActive,line:isLine}">{{message}}</div>
<div :class="getClasses()">{{message}}</div>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '类名',
isActive: true,
isLine: true
},
methods: {
getClasses: function() {
return {
active: this.isActive,
line: this.isLine
}
}
}
});
</script>
</body>
16.(掌握)v-bind动态绑定class(数组语法)
- 数组语法的含义是:class后面跟的是一个数组。
-
Hello World
17.作业v-bind和v-for结合
- 两种方法实现v-for不同行点击效果,第二种好
<body>
<div id="app">
<ul>
:class="{active:isActive[index]}"方法:
<li v-for="(item,index) in movies" :class="{active:isActive[index]}" @click = "changeColor(index)">{{item}}</li>
index==this.num?"active":''方法:
<li v-for="(item,index) in movies" :class="getClass(index)" @click = "changeNum(index)">{{item}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
movies:['龙猫','千与千寻','功夫','天空之城'],
// classes:['c1','c2','c3','c4'],
isActive:[true,false,false,false],
num:0
},
methods:{
changeColor:function(index){
// console.log(index);
this.isActive = [false,false,false,false]
this.isActive[index] = true;
},
changeNum:function(index){
this.num = index;
},
getClass:function(index){
return index==this.num?"active":''
}
}
});
</script>
</body>
18.v-bind动态绑定style(对象语法)
- key:value,value如果不是一个变量,需要加单引号''
<div id="app">
<h2 :style="{color:'red',fontSize:'100px',backgroundColor:color}">{{message}}</h2>
<h2 :style="getStyles()">{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
message:'龙猫',
color:'blue'
},
methods:{
getStyles:function(){
return {color:'red',fontSize:'100px',backgroundColor:this.color}
}
}
});
</script>
19.v-bind动态绑定style(数组语法)
-
(用得很少)
20.(掌握)计算属性computed的基本使用
- 计算属性一般用在对数据进行某种变换对时候,对数据重新定义一个属性
<div id="app">
<h2>{{firstName +' '+lastName}}</h2>
<h2>{{firstName}} {{lastName}}</h2>
<h2>{{getFullName()}}</h2>
<!-- 计算属性一般用在对数据进行某种变换对时候,对数据重新定义一个属性 -->
<h2>{{fullName}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
firstName:'sen',
lastName:'wang'
},
methods:{
getFullName:function(){
return this.firstName+' '+this.lastName
}
},
// 因为说计算属性,名字最好只像属性
computed:{
fullName:function(){
return this.firstName+' '+this.lastName
}
}
});
</script>
21.(掌握)计算属性的复杂操作
- computed用于计算属性,一般写名词eg:计算几本书的总价
<div id="app">
<h2>{{getSumPrice()}}</h2>
<h2>{{SumPrice}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
books:[
{id:1101 ,name:"哈姆雷特" ,price:102},
{id:1102 ,name:"计算机原理" ,price:102},
{id:1103 ,name:"操作系统" ,price:102},
{id:1104 ,name:"web前端" ,price:102},
],
},
methods:{
// 有错误,不知道为什么
getSumPrice:function(){
let sum = 0
for( let i = 0;i<this.books.length;i++){
sum += this.books[i].price;
}
return sum
}
},
computed:{
SumPrice:function(){
// for( let i = 0;i<this.books.length;i++){
// this.sumPrice += this.books[i].price;
// }
// return this.sumPrice;
// 或者使用es6语法:
let sum = 0
for(let book of this.books){
sum += book.price
}
return sum;
}
}
});
</script>
22.课堂回顾(略)
Day 02
01.理解计算属性的setter和getter
- 默认使用get方法,没有用set方法
- set方法只有在设置属性的时候才会调用,如:app.fullName2 = 'sss'
<div id="app">
<h2>{{fullName}}</h2>
<h2>get方法:{{fullName2}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
firstName:'sen',
lastName:'wang',
},
methods:{},
computed:{
fullName:function(){
return this.firstName +' '+ this.lastName;
},
fullName2:{
set:function(newValue){
console.log('---',newValue);
const names = newValue.split(' ');
this.firstName = names[0];
this.lastName = names[1];
},
get:function(){
return this.firstName +' '+ this.lastName
}
}
}
});
</script>
02.计算属性computed和methods对比
- computed只调用一次函数性能高(内部有一个缓存),methods会多次调用函数
<div id="app">
<!-- 1.直接拼接 -->
<h2>{{firstName}}{{lastName}}</h2>
<!-- 2.通过定义methods -->
<h2>{{getFullName()}}</h2>
<h2>{{getFullName()}}</h2>
<h2>{{getFullName()}}</h2>
<h2>{{getFullName()}}</h2>
<!-- 3.通过定义computed -->
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
firstName:'sen',
lastName:'wang'
},
methods:{
getFullName:function(){
console.log('getFullName');//输出(调用)4次
return this.firstName +' '+ this.lastName
}
},
computed:{
fullName:function(){
console.log('fullName');//只输出(调用)1次
return this.firstName +' '+ this.lastName
}
}
});
</script>
03.(掌握)块级作用域let和var
- JavaScript 闭包。它使得函数拥有私有变量变成可能。
<script>
// 1.var定义的变量在块级外边也可以访问
{
var name = "longmao"
console.log(name);
}
console.log(name);
if(true){
var name2 = 'why'
}
console.log(name2);
</script>
- 闭包案例
- 没有使用闭包之前,单击每个按钮打印点击了第i个按钮会全部打印最后1个i的值
- 使用闭包之后,相当于函数套函数,打印的是5个带参数的函数,在点击的时候再传递参数
var btns = document.querySelectorAll('button');
for(i=0;i<btns.length;i++){
// btns[i].onclick = function(){
// console.log('第'+i+'个按钮被点击了');
// }
//使用闭包可以解决此问题,闭包可以看作打印了5个函数,每个函数的num都是一个参数,点击的时候再传递参数
(function(num){
btns[i].onclick = function(){
console.log('第'+num+'个按钮被点击了');
}
})(i)
}
- let块级作用域解决冲突
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
var btns = document.querySelectorAll('button');
for(let i=0;i<btns.length;i++){
btns[i].onclick = function(){
console.log('第'+i+'个按钮被点击了');
}
}
// //使用let:
// {//i = 0
// btns[i].onclick = function(){
// console.log('第'+i+'个按钮被点击了');
// }
// }
// {//i = 1
// btns[i].onclick = function(){
// console.log('第'+i+'个按钮被点击了');
// }
// }
// {//i = 3
// btns[i].onclick = function(){
// console.log('第'+i+'个按钮被点击了');
// }
// }
// //使用var i=3
// {//i = 3
// btns[i].onclick = function(){
// console.log('第'+i+'个按钮被点击了');
// }
// }
// {//i = 3
// btns[i].onclick = function(){
// console.log('第'+i+'个按钮被点击了');
// }
// }
// {//i = 3
// btns[i].onclick = function(){
// console.log('第'+i+'个按钮被点击了');
// }
// }
</script>
04.三种方案对比
- var错误的方式
- var+闭包方式
- let方式
05.(掌握)const的使用和注意点
- ES6开发中,优先使用const,只有需要改变某个标识符时才用let
<script>
const name = 'why';
name = 'what';//报错
</script>
- const obj = { },可以修改obj内部的属性,但是不能修改obj外部的指向:
<script>
const name = 'why';
// name = 'what';//报错
const obj = {
name:'龙猫',
age:2,
move:function(){
console.log(this.name);
}
};
// obj = {}//报错
obj.name = '千寻';
obj.move();//打印的是“千寻”,因为内部的属性可以修改,但是不能改变obj的指向
</script>
06.(掌握)ES6对象字面量增强写法
- ES5和ES6的区别:
-
let name = 'why'; let age = 18; let obj1 = { name:name, age:age, eat:function(){ console.log("吃1"); } } console.log(obj1); obj1.eat(); //ES6 let obj2 = { name,age, eat(){ console.log("吃2"); } } console.log(obj2); obj2.eat();
07.空
08.(掌握)v-on的基本使用和语法糖
- 语法糖v-on:和@等价
09.(掌握)v-on的参数传递问题
- 方法加()的问题
-
如果方法中没有传递参数,可以省略括号(也可以不省略)
+
-
如果方法中有参数传递,必须加括号
+
-
如果方法中有多个参数:
<div id="app">
<!-- 1.事件调用方法没有参数 -->
<button @click = "btn1Click">按钮1不带参数,正常执行</button>
<button @click = "btn1Click()">按钮1()不带参数,正常执行</button>
<!-- 2.事件调用方法有参数 -->
<button @click = "btn2Click('参数')">按钮2(name)带参数,正常返回</button>
<button @click = "btn2Click()">按钮2()带括号但是不传递参数,返回undefined</button>
<button @click = "btn2Click">按钮2不带括号也不传递参数,返回MouseEvent对象</button>
<!-- 3.传递多个参数 -->
<button @click = "btn3Click('参数3',$event)">按钮3,方法定义时需要参数和event</button>
</div>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{},
methods:{
btn1Click(){
console.log("按钮1");
},
btn2Click(name){
console.log(name);
},
//默认不写参数的时候,执行的是event对象:
// btn2Click(event){
// console.log(event);
// }
btn3Click(name,event){
console.log(name,event);
}
}
});
</script>
10.(掌握)v-on修饰符的使用
- vue中阻止事件冒泡: 按钮
- vue中阻止submit默认行为.prevent,如
- vue中键盘按下修饰符@keyup.enter = "keyUp"
- vue中只运行一次修饰符.once,如按钮2
<div id="app">
<!-- 1..stop修饰符 -->
<div @click = "divClick">
<button @click.stop = "btnClick">按钮</button>
</div>
<!-- 2..prevent事件修饰符的使用 -->
<form action="baidu">
<input type="submit" value="提交" @click.prevent = "submitClick">
</form>
<!-- 3.监听键帽的按下 -->
<input type="text" @keyup.enter = "keyUp">
<!-- 4..once修饰符 -->
<button @click.once = "btn2Click">按钮2</button>
</div>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{},
methods:{
btnClick(){
console.log("btnClick");
},
divClick(){
console.log("divClick");
},
submitClick(){
console.log("submitClick");
},
keyUp(){
console.log("keyUp");
},
btn2Click(){
console.log("btn2Click");
}
}
});
</script>
11.(掌握)v-if和v-if-else和v-else的使用
- 区别如下,不太建议在这写,建议在computed里边写判断
<div id="app">
<h2 v-if="score>90">优秀</h2>
<h2 v-else-if="score>80">良好</h2>
<h2 v-else-if="score>60">中等</h2>
<h2 v-else>差</h2>
<h1>{{resultMessage}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
score:88
},
methods:{},
computed:{
resultMessage(){
let showMessage = '';
if(this.score>90){
showMessage = "优秀"
}else if(this.score>80){
showMessage = "良好"
}else if(this.score>60){
showMessage = "中等"
}else{
showMessage = "差"
}
return showMessage;
}
}
});
</script>
12.(掌握)v-if登陆切换的小案例
<div id="app">
<span v-if="isShow">
<label for="username">用户名登录</label>
<input type="text" placeholder="请输入用户名" id="username">
</span>
<span v-else>
<label for="email">邮箱登录</label>
<input type="text" placeholder="请输入邮箱" id="email">
</span>
<button @click="changeLoggin">登录切换</button>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isShow: true
},
methods: {
changeLoggin() {
this.isShow = !this.isShow
}
}
});
</script>
13.(理解)登录切换的input复用问题
- 问题:点击切换的时候,input的内容没有清空
- 原理:div--虚拟dom(放内存里)--显示内容
- 出于性能考虑,因为没有改变label和input元素标签,会继续复用虚拟dom的label和input元素标签
- 解决:各加一个key属性,
- key = "username"
- key = "email"
<span v-if="isShow">
<label for="username">用户名登录</label>
<input type="text" placeholder="请输入用户名" id="username" key="username">
</span>
<span v-else>
<label for="email">邮箱登录</label>
<input type="text" placeholder="请输入邮箱" id="email" key = "email">
</span>
<button @click="changeLoggin">登录切换</button>
14.(掌握)v-show的使用和v-if的区别
- v-show:false相当于加了一个行内样式display:none属性
- v-if:false相当于删除dom
<div id="app">
<!-- v-if为false时,相当于删除dom,适合使用频率低的时候 -->
<h2 v-if="isShow" id="if">{{message}}</h2>
<!-- v-show为false时,相当于display:none,适合使用频率高的时候 -->
<h2 v-show="isShow" id="show">{{message}}</h2>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: "hello",
isShow: true
},
methods: {}
});
</script>
15.(掌握)v-for遍历数组和对象
- 遍历数组
<div id="app">
<ul>
<li v-for="(item,index) in names">{{index+1}},{{item}}</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
names: ['why', 'wangsen', 'james', 'curry']
},
methods: {}
});
</script>
- 遍历对象
<div id="app">
<!-- 1.笨方法 -->
<ul>
<li>{{obj.name}}</li>
<li>{{obj.age}}</li>
<li>{{obj.height}}</li>
</ul>
<!-- 2.遍历方法,只获取value -->
<ul>
<li v-for="item in obj">{{item}}</li>
</ul>
<!-- 3.获取(value,key) -->
<ul>
<li v-for="(item,key) in obj">{{key}}:{{item}}</li>
</ul>
<!-- 4.获取(value,key,index)index用的很少 -->
<ul>
<li v-for="(item,key,index) in obj">{{key}}:{{item}}--{{index}}</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
obj: {
name: 'wangsen',
age: 18,
height: 1.88
}
},
methods: {}
});
</script>
16.(理解)v-for绑定和非绑定key的区别
- 数组插入app.letters.splice(2,0,'F') ,第二个字母边插入F
- 正常情况是虚拟dom里插入F,然后渲染。但是实际不是,实际是把E改成F,把D改成C,E改成D,最后加一个E,性能比较低,这叫diff算法
- 所以官方推荐我们使用v-for时,给对应元素加:key属性,这样diff算法就可以正确识别此节点,找到正确的位置插入
- :key="item"不用index是因为插入之后,index会变,不是一一对应,性能不好
<div id="app">
<ul>
<!-- :key="item"不用index是因为插入之后,index会变,不是一一对应,性能不好-->
<li v-for="(item,index) in letters" :key="item">{{index}},{{item}}</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
letters: ['A', 'B', 'C', 'D', 'E']
},
methods: {}
});
</script>
17.(掌握)数组中哪些是响应式的
数组方法 | 是否响应式 | 作用 |
this.letter.push('sss') | 响应式 | 数组后边添加元素,可以做到响应式 |
this.letters.pop() | 响应式 | 删除数组最后一个 |
this.letters.shift() | 响应式 | 删除数组中的第一个 |
this.letters.unshift('aaa', 'bbb') | 响应式 | 在数组前边添加元素,可以传多个参数是因为是可变参数...str |
this.letter.sort() | 响应式 | 排序,响应式 |
this.letters.reverse() | 响应式 | 数组反转 |
this.letters.splice(2, 1, 'm ') | 响应式 | 可以做增删改,从第二个开始,删除1个,增加‘m’ |
this.letters[0] = 'bbbb' | 否 | 不能做到响应式,可以this.letters.splice(0, 1, 'bbbb');替代 |
Vue.set(this.letters, 0, 'bbbb') | 响应式 | vue的方法实现元素替换 |
18.(掌握)作业的回顾和实现
- 四行一列的数据,点击谁,谁变红
- 重点:currentIndex的引入(此类问题都是这么解决),就是获取当前点击的index的数字
<div id="app">
<ul>
<li v-for="(item,index) in movies" @click="changeClass(index)" :class="{active:currentIndex==index}">{{item}}</li>
<!-- <li v-for="(item,index) in movies" @click="changeClass(index)" :class="{active:currentIndex==0}">{{item}}</li>
<li v-for="(item,index) in movies" @click="changeClass(index)" :class="{active:currentIndex==1}">{{item}}</li>
<li v-for="(item,index) in movies" @click="changeClass(index)" :class="{active:currentIndex==2}">{{item}}</li>
<li v-for="(item,index) in movies" @click="changeClass(index)" :class="{active:currentIndex==3}">{{item}}</li> -->
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
currentIndex: 0,
movies: ['海王', '海贼王', '加勒比海盗', '海尔兄弟']
},
methods: {
changeClass(index) {
this.currentIndex = index;
}
}
});
</script>
19.(掌握)购物车案例-界面搭建
- v-for把书籍遍历
<table>
<thead>
<tr>
<th></th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in bookList">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td>
<span>{{getFinallPrice(item.price)}}</span>
<td>
<button @click="subCount(index)">-</button>
<span>{{item.count}}</span>
<button @click="addCount(index)">+</button>
</td>
<td>
<button @click="remove(index)">移除</button>
</td>
</tr>
</tbody>
</table>
<h2>总价格:{{sumPrice}}</h2>
20.(掌握)购物车案例-过滤器filter的使用
- 没有使用过滤器和使用过滤器都能达到目的:
<!--没有使用过滤器-->
<span>{{getFinallPrice(item.price)}}</span>
<script>
...
methods: {
getFinallPrice(price) {
return '¥' + price.toFixed(2)
}
},
</script>
<!--使用过滤器-->
<span>{{item.price | showPrice}}</span>
<script>
...
methods: {
},
filters: {
showPrice(price) {
return '¥' + price.toFixed(2)
}
},
</script>
21.(掌握)购物车案例-改变购买数量
- :disabled="item.count==1"剩下1个点时候,减少按钮不能再点击(变灰)
<th>
<button @click = "sub(index)" :disabled="item.count==1">- </button>
<span>{{item.count}}</span>
<button @click = "add(index)">+</button>
</th>
...
methods:{
sub(index){
this.books[index].count--
},
add(index){
this.books[index].count++
},
}
22.(掌握)购物车案例-移除按钮-最终价格
- 移除全部书之后,购物车变成“购物车为空”,利用v-if和v-else
- 计算总价使用计算属性:
两种方法 DOM中区别 调用 computed 总价为{{sumPrice}}
会自动调用sumPrice方法 methods 总价为{{sumPrice()}}
也会自动调用sumPrice方法,但是不太合适 totalPrice() { //计算总价的方法1: // let totalprice = 0; // for (item of this.bookList) { // totalprice += item.price * item.count; // } // return totalprice //计算总价的方法2(高级函数): return this.bookList.reduce(function(preValue, book) { return preValue + book.price * book.count }, 0) }
<div v-if="books.length!=0">
<table>books</table>
</div>
<div v-else>购物车为空</div>
Day 03
01.(掌握)JavaScript高阶函数的使用
- 计算总价的三种方法(of 最好):
sumPrice(){
//1.普通的for循环
// let sumprice = 0;
// for(let i=0;i<this.books.length;i++){
// sumprice += this.books[i].price * this.books[i].count;
// }
// return sumprice
// 2. in循环
// let sumprice = 0;
// for(let i in this.books){
// sumprice += this.books[i].price * this.books[i].count;
// }
// return sumprice
// 3.of循环
let sumprice = 0;
for(let item of this.books){
sumprice += item.price * item.count;
}
return sumprice
}
编程范式 | 命令式编程 | 声明式编程 |
编程范式 | 面向对象编程 (第一公民:对象) | 函数式编程 (第一公民:函数) |
- 高阶函数:函数所需要的参数也是函数
- filter / map / reduce高级方法(数组):
- filter的使用,用于过滤数值,不改变数值
nums = [12 , 333 , 34 , 55 , 133 ] let new3Nums = nums.filter(function(n) { return n < 100 }) console.log(new3Nums) //打印[12 , 34 , 55 ] |
2.map的使用,一般用于数值操作,不过滤数值
let new4Nums = new3Nums.map(function(n) { return n * 2 }) console.log(new4Nums); //打印[24 , 68 , 110 ] |
3.reduce应用,用于数组求和:
let total2 = new4Nums.reduce(function(total, num) { return total + num; }) console.log(total2); //打印24+68+110的和 |
4.命令式和高级函数对比代码:
const nums = [10, 20, 44, 66, 888, 123, 55, 75, 111];
//1.取出所有小于100的数字
let newNums = [];
for (let n of nums) {
if (n < 100) {
newNums.push(n)
}
}
console.log(newNums);
//2.将所有小于100的数字进行转化:全部*2
let new2Nums = [];
for (let n of newNums) {
new2Nums.push(n * 2);
}
console.log(new2Nums);
//3.将所有num2Nums数字相加,得到最终结果
let total = 0
for (let n of new2Nums) {
total += n;
}
console.log(total);
//4.filter,根据条件筛选,不能改变值
let new3Nums = nums.filter(function(n) {
return n < 100
})
console.log(new3Nums);
//5.map,不做筛选,改变值
let new4Nums = new3Nums.map(function(n) {
return n * 2
})
console.log(new4Nums);
// 6.reduce
let total2 = new4Nums.reduce(function(total, num) {
return total + num;
})
console.log(total2);
//7.一行代码搞定,函数式编程
let total3 = nums.filter(function(n) {
return n < 100
}).map(function(n) {
return n * 2
}).reduce(function(total, num) {
return total + num;
})
console.log(total3);
//8.一行代码搞定——箭头函数
let total4 = nums.filter(n => n < 100).map(n => n * 2).reduce((total, num) => total + num);
console.log(total3);
02.(掌握)v-model的使用和原理
- v-model指令来实现表单元素和数据的双向绑定
- v-bind指令是单向绑定,只能从数据传到表单,表单的值改变不能改变message的值
1.双向绑定,可以接收和改变message:
<input type="text" v-model="message">
<h2>message:{{message}}</h2>
2.单向绑定,只能接收不能改变message:
<input type="text" :value="message">
3.人为单向绑定,只改变不接收,2+3合起来就是v-model
<input type="text" v-on:input="valueChange">
4.手动双向绑定:
<input type="text" :value="message" @input="message=$event.target.value">
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '龙猫'
},
methods: {
valueChange(event) {
this.message = event.target.value;
}
}
});
</script>
03.(掌握)v-model结合radio类型使用
- 二选一的时候和value有关
- 单选多时候是boolean
<label for="male">
<input type="radio" id="male" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">女
</label>
<h2>你选择的性别是:{{sex}}</h2>
<h1>单选框:</h1>
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意
</label>
<h2>你选择的是:{{isAgree}}</h2>
<button :disabled="!isAgree">下一步</button>
......
data:{
sex:'男',
isAgree:false
},
04.(掌握)v-model结合checkbox类型
- 多选多时候绑定的是数组
<h1>多选框:</h1>
<input type="checkbox" value="篮球" v-model="hobbies">篮球
<input type="checkbox" value="钢琴" v-model="hobbies">钢琴
<input type="checkbox" value="跳舞" v-model="hobbies">跳舞
<h2>你的爱好是:{{hobbies}}</h2>
...
var app=new Vue({
el:'#app',
data:{
hobbies:[],
},
});
05.(掌握)v-model结合select类型使用
- 多选的时候加multiple
<div id="app">
<h2>选择一个:</h2>
<select name="abc" id="" v-model="fruit">
<option value="苹果" >苹果</option>
<option value="香蕉" >香蕉</option>
<option value="梨" >梨</option>
<option value="榴莲" >榴莲</option>
</select>
<h2>{{fruit}}</h2>
<h2>选择多个:</h2>
<select name="abc" id="" v-model="fruits" multiple>
<option value="苹果" >苹果</option>
<option value="香蕉" >香蕉</option>
<option value="梨" >梨</option>
<option value="榴莲" >榴莲</option>
</select>
<h2>{{fruits}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
fruit:'榴莲',
fruits:[]
},
});
</script>
06.(掌握)input中的值的绑定
- v-for实现多选
<h1>v-for:</h1>
<label v-for="item in originHobbies" :for="item">
<input type="checkbox" :value="item" v-model="hobbies">{{item}}
</label>
<h2>{{hobbies}}</h2>
var app=new Vue({
el:'#app',
data:{
hobbies:[],
originHobbies:["篮球","钢琴","跳舞","瑜伽","游泳"],
},
});
07.(掌握)v-model修饰符的使用
- .lazy回车之后才会改变message值
- .number让输入的值由字符串改为number类型
- trim去除两端多余空格
<div id="app">
<h1>1.修饰符lazy</h1>
<input type="text" v-model.lazy="message">
<h2>{{message}}</h2>
<h1>2.修饰符number</h1>
<input type="number" v-model.number="age">
<h2>{{typeof age}},{{age}}</h2>
<h1>3.trim去除空格</h1>
<input type="text" v-model.trim="name">
<h2>{{name}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
message:'',
age:18,
name:''
},
methods:{}
});
</script>
08.(掌握)组件化的使用和实现步骤
09. (掌握)组件化的基本使用过程
- 创建组件构造器对象
- 注册组件
- 使用组件
<div id="app">
<h2>我是内容</h2>
<p>我是内容1111</p>
<p>我是内容2222</p>
<h2>我是内容</h2>
<p>我是内容1111</p>
<p>我是内容2222</p>
<!-- 3.使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
//1.创建组件构造器对象
const cpnC = Vue.extend({
template:
`<div>
<h2>我是内容</h2>
<p>我是内容3333</p>
<p>我是内容4444</p>
</div>`
});
//2.注册组件
Vue.component('my-cpn',cpnC);
var app=new Vue({
el:'#app',
data:{},
methods:{}
});
</script>
10.(掌握)全局组件和局部组件
- Vue CLI3.x 构建Vue项目的
- 使用组件必须在app元素内
- 全局组件和局部组件
- 全局组件:Vue.component('my-cpn',cpnC); app1和app2都可以使用
- 局部组件:components:{ my_cpn:cpnC }, /只能在对应的app下面使用,my_cpn使用组件的标签名, 是在vue实例中创建的,开发中用局部组件比较多。
- 注意:组件名不能大写
<div id="app">
<!-- <my-cpn></my-cpn> -->
<my_cpn></my_cpn>
</div>
<div id="app2">
<!-- <my-cpn></my-cpn> -->
局部组件不能在这里使用:
<!-- <my_cpn></my_cpn> -->
</div>
<script src="../js/vue.js"></script>
<script>
//1.全局组件
const cpnC = Vue.extend({
template:`
<div>
<div>我是标题</div>
<p>我是内容</p>
</div>
`
})
//全局组件,可以在多个vue的实例下面使用
//Vue.component('my-cpn',cpnC);
var app=new Vue({
el:'#app',
data:{},
components:{
//局部组件,只能在对应的app下面使用,my_cpn使用组件的标签名
my_cpn:cpnC
},
methods:{}
});
var app2=new Vue({
el:'#app2',
data:{},
methods:{}
});
</script>
11.(掌握)父组件和子组件的区分
- 父组件也可以套子组件(套娃):Vue.extend({... conponents:{cpn1:cpnC1}})
- new Vue({});也可以看成是最顶层的组件(root),里边也可以有template属性
<div id="app">
<cpn2></cpn2>
<!-- 下边这个会报错,因为要么全局注册,要么组件中注册过局部的 -->
<cpn1></cpn1>
</div>
<script src="../js/vue.js"></script>
<script>
const cpnC1 = Vue.extend({
template:`
<div>
<h2>我说标题1</h2>
<p>我说内容1</p>
</div>`
});
const cpnC2 = Vue.extend({
template:`
<div>
<h2>我说标题2</h2>
<p>我说内容2</p>
<cpn1></cpn1>
</div>`,
components:{
cpn1:cpnC1
}
});
var app=new Vue({
el:'#app',
data:{
},
components:{
cpn2:cpnC2
},
methods:{}
});
</script>
12.(掌握)注册组件的语法糖写法
-
语法糖省去了调用Vue.extend()带步骤,可以直接使用一个对象代替
<div id="app">
<cpnc1></cpnc1>
<cpnc2></cpnc2>
</div>
<script src="../js/vue.js"></script>
<script>
//1.创建组件的构造器
// const cpn1 = Vue.extend()
//2.注册组件(语法糖):
Vue.component('cpnc1',{
template:`
<div>
<h2>我是标题1</h2>
<p>我是内容1</p>
</div>`
})
var app=new Vue({
el:'#app',
data:{},
components:{
cpnc2:{
template:`
<div>
<h2>我是标题2</h2>
<p>我是内容2</p>
</div>`
}
},
methods:{}
});
</script>
13.(掌握)组件模板抽离的写法
- 模板分离的第一种写法:
- 模板分离的第二种写法: ,第二个比较好,用得多
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<!-- 1.模板分离的写法 -->
<script type="text/x-template" id="cpnC1">
<div>
<h2>模板分离1</h2>
<p>我是内容1</p>
</div>
</script>
<!--2.模板分离的第二种写法 -->
<template id="cpnC2">
<div>
<h2>模板分离2</h2>
<p>我是内容2</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn1',{
template:'#cpnC1'
})
Vue.component('cpn2',{
template:'#cpnC2'
})
var app=new Vue({
el:'#app',
data:{},
methods:{}
});
</script>
14.(理解)为什么组件data必须是函数
- 组件内部不能访问vue实例中的内容的:
{{title}}
......data:{title:"我是标题"} - 如果想访问,必须加data(){return {}}: Vue.component('cpn1',{
template:'#cpnC1',
data(){
return {
title:"我是标题",
con:"我是内容"
}
}
})
<div id="app">
<cpn1></cpn1>
</div>
<template id="cpnC1">
<div>
<!-- 下边title不能访问实例数据 -->
<h2>{{title}}</h2>
<p>{{con}}</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn1',{
template:'#cpnC1',
data(){
return {
title:"我是标题",
con:"我是内容"
}
}
})
var app=new Vue({
el:'#app',
data:{
//以下这种写法错误
//title:"我是标题"
},
methods:{}
});
</script>
- let obj1 = abc();和let obj2 = abc();并不是一个对象,指向的地址也不一样,因此,data(){return {count:0}}每次返回的count(局部变量)是不同对象的count,互不冲突,如果用data():{count:obj},就会冲突:
<div id="app">
<!-- 组件实例对象 -->
<cpn1></cpn1>
<cpn1></cpn1>
<cpn1></cpn1>
</div>
<template id="cpnC1">
<div>
<button @click="sub">-</button>
<span>{{count}}</span>
<button @click="add">+</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
//1.创建组件
Vue.component('cpn1',{
template:'#cpnC1',
data(){
//每次调用的时候,return一个新的对象,这样避免的用同一个count导致的冲突
//注:这里return的对象是局部变量,每次是不一样的,如果外边定义个obj穿过来,就会导致冲突了(使用同一个count)
return {
count:0
}
},
methods:{
sub(){
this.count--
},
add(){
this.count++
}
}
})
var app=new Vue({
el:'#app',
data:{},
});
</script>
15.(掌握)父子组件通信-父传子
- 父传子:通过props向子组件传递数据(properties属性)
- 子传父:自定义emit事件
- 理解:属性向下传,方法向上传
- 说明:new Vue({})相当于根组件
- 父传子 (props的值有多种类型:对象、数据、字符串、布尔、数值等等)
<div id="app">
<cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpnC1">
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<p>{{cmessage}}</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template:'#cpnC1',
// props:['cmovies','cmessage'],//开发中数组的写法比较少
//props不止是数组,还可以是对象、字符串、布尔、数组点等,如:
props:{
//1.类型限制
// cmovies:Array,
// cmessage:String
//2.提供一些默认值
cmovies:{
type:Array,
default(){ //类型是对象或者数组时,默认值必须是一个函数
return []
}
},
cmessage:{
type:String,
default:'aaa',
required:true //使用组件的时候,必须传cmessage属性
}
},
data(){
return {}
},
methods:{
}
}
var app=new Vue({
el:'#app',
data:{
movies:['龙猫','千与千寻','天空之城'],
message:'你好'
},
components:{
// cpn:cpn
cpn //增强写法
},
});
</script>
16.(掌握)父子组件通信-props驼峰标识
- props里边要用驼峰命名(cInfo),外边使用组件的时候,要用-号(c-info)
- 知识点补充:js大小写敏感,html大小写不敏感
- 父组件传递数据的过程:1,父组件data中准备数据,2,子组件的构造器添加props,包括需要用到的变量,在template中使用这些变量,3,在vue实例中把这两项绑定起来
<div id="app">
<cpn :c-info="info" :child-my-message="childmymessage"></cpn>
<!-- 下面这个写只输出默认值{} -->
<!-- <cpn></cpn> -->
</div>
<template id="cpn">
<div>
<h2>{{cInfo.name}}</h2>
<h2>{{cInfo.age}}</h2>
<h2>{{cInfo.height}}</h2>
<h2>{{childMyMessage}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template:'#cpn',
props:{
cInfo:{
type:Object,
default(){
return {}
}
},
childMyMessage:{
type:String
}
}
}
var app=new Vue({
el:'#app',
data:{
info:{
name:"龙猫",
age:2,
height:2.88
},
childmymessage:"aaaa",
},
components:{
cpn
},
methods:{}
});
</script>
17.(掌握)父子组件通信-子传父(自定义事件)
- 使用场景:1,购物网站列表中选择手机,会向父级发消息说点击了谁,然后父级再回转手机的数据;
-
传递过程:1,子组件中通过$emit()来触发事件,2,父组件中v-on来监听子组件事件
-
注:父组件中的方法不要写参数,默认在子组件模版中已经定义了,不能写cpnClick(item),只能写cpnClick
<!-- 父组件模板 -->
<div id="app">
<!-- 不能写cpnClick(item) 相当于之前的@click="btnClick"默认会传递event事件,这里默认传item -->
<cpn1 @item-click="cpnClick"></cpn1>
</div>
<!-- 子组件模板 -->
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
//1.子组件
const cpn1 = {
template:'#cpn',
data(){
return {
categories:[
{id:'aaa',name:'热门推荐'},
{id:'bbb',name:'手机数码'},
{id:'ccc',name:'家用家电'},
{id:'ddd',name:'电脑办公'},
]
}
},
methods:{
btnClick(item){
// 自定义事件item-click,相当于之前的reset-map
//传递过程:1,子组件中通过$emit()来触发事件,2,父组件中v-on来监听子组件事件
this.$emit('item-click',item)
}
}
}
//2.父组件
var app=new Vue({
el:'#app',
data:{
},
components:{
cpn1
},
methods:{
cpnClick(item){
console.log(item);
}
}
});
</script>
- 计数器小案例,把子组件中的count传递给父组件total:
<div id="app">
<cpn @cpn-sub="changeTotal" @cpn-add="changeTotal"></cpn>
<h2>{{total}}</h2>
</div>
<template id="cpn">
<div>
<button @click="sub">-</button>
<button @click="add">+</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template:'#cpn',
data(){
return {
count:0
}
},
methods:{
sub(){
this.count--;
this.$emit('cpn-sub',this.count)
},
add(){
this.count++;
this.$emit('cpn-add',this.count)
}
}
}
var app=new Vue({
el:'#app',
data:{
total:0
},
components:{
cpn
},
methods:{
changeTotal(count){
this.total = count
}
}
});
</script>
18.(了解)项目展示
- template和JavaScript代码相互分离
19.(了解)知识回顾
04day
01.(掌握)父子组件通信-结合双向绑定案例
- 子组件使用v-model案例
- v-model相当于v-bind:value和@input="$event.target.value"的结合,也就是set方法
<div id="app">
<cpn :number1="num1"
:number2="num2"
@num1-change="changeNum1"
@num2-change="changeNum2"
></cpn>
</div>
<template id="cpn">
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<!-- <input type="text" v-model="dnumber1"> -->
<input type="text" :value="dnumber1" @input ="num1Input">
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<!-- <input type="text" v-model="dnumber2"> -->
<input type="text" :value="dnumber2" @input ="num2Input">
</div>
</template>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
num1:1,
num2:0
},
components:{
cpn:{
template:'#cpn',
props:{
number1:{
type:Number
},
number2:{
type:Number
},
},
data(){
return {
dnumber1:this.number1,
dnumber2:this.number2
}
},
methods:{
num1Input(){
//1.input中的value复制到dnumber1中
this.dnumber1 = event.target.value;
//2.为了让父组件可以修改值,发出一个事件
this.$emit('num1-change',this.dnumber1);
//3.让number2和dnumber2的值是1的100倍
this.dnumber2 = this.dnumber1 * 100;
this.$emit('num2-change',this.dnumber2)
},
num2Input(){
this.dnumber2 = event.target.value;
this.$emit('num2-change',this.dnumber2);
//3.让number2和dnumber2的值是1的100倍
this.dnumber1 = this.dnumber2 / 100;
this.$emit('num1-change',this.dnumber1)
},
}
},
},
methods:{
changeNum1(value){
this.num1 = parseInt(value);
},
changeNum2(value){
this.num2 = parseInt(value);
}
}
});
</script>
02.(掌握)上边代码的图示:
03.(了解)结合双向绑定案例-watch实现
- watch可以监听某一个属性的改变
- watch是实时监听属性变化,computed是计算属性,计算完后有一个缓存。而methods监听触发事件的。
- watch是监听变化的,不变化不触发
<div id="app">
<cpn :number1="num1"
:number2="num2"
@num1-change="changeNum1"
@num2-change="changeNum2"
></cpn>
</div>
<template id="cpn">
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<!-- <input type="text" v-model="dnumber1"> -->
<input type="text" v-model="dnumber1">
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<!-- <input type="text" v-model="dnumber2"> -->
<input type="text" v-model="dnumber2">
</div>
</template>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
num1:1,
num2:0
},
components:{
cpn:{
template:'#cpn',
props:{
number1:Number,
number2:Number,
},
data(){
return {
dnumber1:this.number1,
dnumber2:this.number2
}
},
watch:{
dnumber1(newValue){
this.dnumber2 = newValue * 100;
this.$emit('num1-change',newValue)
},
dnumber2(newValue){
this.dnumber1 = newValue / 100;
this.$emit('num2-change',newValue)
},
},
},
},
methods:{
changeNum1(value){
this.num1 = parseInt(value);
},
changeNum2(value){
this.num2 = parseInt(value);
}
}
});
</script>
04.(掌握)父组件访问子组件-$children-$refs
- 父组件访问子组件两种方式
- 注意:refs是渲染结束后才能拿到,不能在初始化的时候访问
第一种(用得少) | 第二种(用的非常多) |
$children | $refs (refs:reference引用的意思) |
结果是个数组,下标志拿数据方式不太好,因为子组件数量可能会变化,下标的索引值会变 | 结果是个对象,可以给每个组件添加一个ref属性设置唯一性 |
<div id="app">
<cpn ref="aaa"></cpn>
<cpn ref="bbb"></cpn>
<cpn ref="ccc"></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>我是子组件</div>
</template>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{},
methods:{
btnClick(){
// 一、$children结果是个数组,用得很少
//1,取子组件的方法,下标志拿数据方式不太好,因为子组件数量可能会变化
this.$children[0].showMessage();//0代表第一个子组件
// //2,取子组件的数据
console.log(this.$children[0].name);
// //3,或者循环遍历
for(let item of this.$children){
item.showMessage();
console.log(item.name);
}
//二、$refs结果是个对象,用的非常多
this.$refs.aaa.showMessage();
console.log(this.$refs.aaa.name);
}
},
components:{
cpn:{
template:'#cpn',
data(){
return {
name:"我是子组件的内容"
}
},
methods:{
showMessage(){
console.log('message');
}
}
}
}
});
</script>
05.(了解)子访问父-parent-root
- this.$parent和this.$root用得也非常少
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
message:'父组件message'
},
components:{
cpn:{
template:'#cpn',
methods:{
btnClick(){
//1,this.$parent返回的不是组件(VueComponent),而是实例(Vue),用的非常少
console.log(this.$parent.name);
//2,this.$root可以访问父,也可以访问父亲父父亲,返回实例对象
console.log(this.$root);
}
}
}
},
methods:{}
});
</script>
06.(掌握)slot插槽的基本使用
- 插槽就是预留的空间,一般抽取共性,保留插槽
- 使用案例:
- 使用过程:template里加入,也可以设置默认值按钮
<div id="app">
<cpn><button>按钮</button></cpn>
<cpn><span>span标签</span></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<!-- 这种定义的组件不具有扩展性,如仅对第一个<cpn></cpn>组件的<p>后加一个span或者button标签-->
<h2>我是组件标题</h2>
<p>我是组件内容</p>
<!-- 下边插槽内加<button>按钮</button>可以设置插槽默认值 -->
<slot><button>按钮</button></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
},
components:{
cpn:{
template:'#cpn',
}
},
methods:{}
});
</script>
07.(掌握)具名插槽的使用
- 当有多个slot,想改变某一个值的时候,如京东导航栏,模板设置name:中间,父组件标题:
<div id="app">
<!-- 1.不替换 -->
<cpn></cpn>
<!-- 2.只会把没有名字的替换掉 -->
<cpn><span>标题</span></cpn>
<!-- 3.只会替换center的slot -->
<cpn><span slot="center">标题</span></cpn>
</div>
<template id="cpn">
<div>
<slot name="left"><span>左边</span></slot>
<slot name="center"><span>中间</span></slot>
<slot name="right"><span>右边</span></slot>
<slot><span>右边</span></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
},
components:{
cpn:{
template:'#cpn',
}
},
methods:{}
});
</script>
08.(理解)编译作用域的概念
- 不关心组件,只关心在那个app里,只会使用vue实例里边的isShow
<div id="app">
<cpn v-show="isShow"></cpn>
</div>
<template id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{
isShow:true //使用的是这里的isShow
},
components:{
cpn:{
template:'#cpn',
data(){
return {
isShow:false
}
}
}
},
methods:{}
});
</script>
09.(掌握)作用域插槽的使用
- 需求:在渲染之前,把组件的数据传给vue实例(渲染之后一般用this.$refs.name)
- 即,父组件对子组件的展示方式不满意,想以另外一种方式展示
- 总结:父组件替换插槽的标签,但是内容由子组件来提供
<div id="app">
<!-- 1.不改变template,直接显示 -->
<cpn></cpn>
<!-- 2.不想用列表的方式,想用-连接字符串 -->
<cpn>
<template slot-scope="slot">
<!-- <span v-for="item in slot.data">{{item}} - </span> -->
<span>{{slot.data.join('-')}}</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<slot :data="pLanguages">
<ul>
<li v-for="item in pLanguages">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
var app=new Vue({
el:'#app',
data:{},
components:{
cpn:{
template:'#cpn',
data(){
return {
pLanguages:['Javascript','c++','Java','Python','go','swift','c#']
}
}
}
},
methods:{}
});
</script>
10.(了解)前端代码复杂带来的问题
- 如小明在a.js文件中定义var flag = true;小红在b.js定义var flag = false;当小明在c.js使用if(flag){}时可能会传小红的flag
- 解决方法匿名函数,即闭包。闭包的核心理念是用函数的变量作用域控制来完成javascript所缺失的块作用域功能:
- 闭包的缺点导致的代码的复用性不高,如下边的sum函数在别的js文件中访问不到,因此闭包也不行
(function(){
var flag = true;
function sum(num1,num2){
return num1+num2
}
})()
11.(理解)前端模块化雏形和CommonJS
- 只能用模块化解决(如下a.js),就可以在c.js使用if(moduleA.flag){},下边这种就是模块化思想,但是已经不用了
- 常见的模块化规范:CommonJS,AMD,CMD,ES6的Modules
var moduleA = (function(){
var obj = {};
var name = '小明';
var age = 18;
function sum(num1,num2){
return num1 + num2
}
var flag = true;
// if(flag){
// console.log(sum(10,20));
// }
obj.name = name;
obj.age = age;
obj.flag = flag;
obj.sum = sum;
obj.myFunction = function(value){
console.log(value)
}
return obj
})()
- CommonJS了解
//a.js导出
module.exports = {
name:ws,
flag:true,
sum(a,b){
return a+b
}
}
//b.js导入,导入名字要和导出的名字一样
let {name, sum, flag} = require('./a.js');
//上边等同于下边
let mod = require('./a.js');
let name = mod.name;
let flag = mod.flag;
let sum = mod.sum;
12.(掌握)ES6模块化的导入和导出
- 可以使用type="module"把js变成块级作用域,解决变量冲突问题:
- 但是这样带来了新的问题,其他模块想使用该块内的变量,访问不到:
//index.html
// <script src="./aaa.js" type="module"></script>
// <script src="./bbb.js" type="module"></script>
// <script src="./ccc.js" type="module"></script>
//aaa.js//小明
var name = 'xiaoming';
var age = 18;
var flag = true;
function sum(a , b){
return a+b
}
if(flag){
console.log(sum(11,2));
}
//bbb.js//小红
var name = 'xiaohong';
var age = 19;
var flag = false;
//ccc.js 报错,flag未定义
if(flag){
console.log('flag');
}
- 解决方案:模块导出(3种方法)
-
方法1,先定义变量、函数或class,再导出:export { flag,sum,Person}
-
方法2,直接导出:export var num1 = 100001; export function sum(a,b){return a+b}; export class Person{run(){console.log("在奔跑")}}
-
方法3,export default name; import myName from "./aaa.js",用于导入者自己可以修改名字,但是export default只能在一个js中使用一次
-
前两种导入方法一样:import {flag , sum,num1 , Person} from "./aaa.js"或import * as obj from "./aaa.js" 然后obj.flag可以取值
//index.html
//<script src="./aaa.js" type="module"></script>
//<script src="./bbb.js" type="module"></script>
//<script src="./ccc.js" type="module"></script>
//aaa.js
var name = 'xiaoming';
var age = 18;
var flag = true;
function sum(a , b){
return a+b
}
if(flag){
console.log(sum(11,2));
}
//1.导出方式一:
export{
flag,sum
}
//2.导出方式二:
export var num1 = 100001;
export var height = 1.88;
export class Person{
run(){
console.log("在奔跑");
}
}
//3.导出方式三:
var address = '北京市';
export default address;
//ccc.js
import {flag , sum} from "./aaa.js"
if(flag){
console.log('flag');
console.log(sum(111,222));
}
import {num1 , height ,Person} from "./aaa.js"
console.log(num1);
console.log(height);
let ws = new Person();
ws.run();
import myAdd from "./aaa.js"
console.log(myAdd);
import * as obj from "./aaa.js"
console.log(obj.height);
13.(理解)webpack的介绍和安装
- webpack就是一个模块打包工具
- webpack打包现在用的比较多,而gulp用的比较少了,grunt更没人用了
- 什么时候用gulp和webpack
- webpack和node、npm关系:
- 注意:Mac安装webpack要先获取权限,使用sudo -s然后输入计算机密码,最后npm install webpack@3.6.0 -g
14.(掌握)webpack的基本使用过程
- 使用目的代码打包到dist文件夹中,方法一的过程(方法二是配置webpack.config.js,见15):
- 全局安装webpack(npm install webpack@3.6.0 -g),终端定位到项目的根目录中
- webpack ./src/main.js ./dist/bundle.js 意思是把main.js打包到dist文件夹下的bundle.js文件中,至于main.js里边的依赖会由webpack自动处理
- index.html中引用bundle.js,,执行index.html
- 代码:
//index.html
//<script src="./dist/bundle.js"></script>
//main.js
//1.common.js导入
let {sum ,mul} = require('./mathUtils.js');
console.log(sum(11,3));
console.log(mul(22,10));
//2.ES6导入
import {name , age , height} from './info';
console.log(name);
console.log(age);
console.log(height);
//mathUtils.js
function sum(num1,num2){
return num1 + num2
}
function mul(num1,num2){
return num1 * num2
}
//1.common.js导出
module.exports = {
sum,
mul
}
//info.js
//2.ES6导出
export const name = 'xiaoming';
export const age = 18;
export const height = 1.88;
15.(掌握)webpack.config.js和package.json的配置
- 代码打包的方法二:
- (第一层映射)新建webpack.config.js文件(如下),输入打包的参数(如入口、出口);2.运行命令:webpack
- (第二层映射)执行npm init 命令,在package.js文件加"build":"webpack" //本句加完之后,能用npm run build替代webpack命令
//webpack.config.js文件
const path = require('path');//'path'是node的系统模块
module.exports = {
entry:'./src/main.js',
output:{
path:path.resolve(__dirname,'dist'),
filename:'bundle.js'
}
}
- 注意:const path = require('path') ,括号内的path是node的系统模块,__dirname是node上下文自带的全局变量
- 运行webpack一般需要依赖node环境的项目,都会有package.json文件(项目的信息,如下),该文件的配置命令:npm init
//package.js文件
{
"name": "meetwebpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build":"webpack" //本句加完之后,能用npm run build替代webpack命令
},
"author": "",
"license": "ISC"
}
- 刚刚的webpack是全局安装的(npm install webpack@3.6.0 -g),但是开发的时候每个项目都要有自己的本地的webpack(npm install webpack@3.6.0 --save-dev),因为二者版本可能不一样
——只要是在终端敲webpack,用的都是全局的,想使用本地的必须./node_modules/.bin/webpack
——所以定义"build":"webpack" 的好处是执行npm run build时,优先找本地的webpack
16.(掌握)webpack中使用css文件的配置(loader)
- loader干什么的?:因为webpack除了要打包js以外,还要对css、图片、ES6、Vue等进行打包,这时候webpack就不支持了,需要对webpack扩展loader就可以了
- loader使用过程:1.安装loader:npm install --save-dev css-loader@2.0.2 2.webpack.config.js配置module.exports={} 3.安装css-loader(可以去webpack官网看安装命令和配置说明):npm install style-loader --save-dev
-
css-loader只负责加载,不负责解析,需要再安装style-loade(样式添加到dom中)
const path = require('path');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '../dist/' //涉及到url的东西,都会再前边拼接dist/
},
module: {
rules: [{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
}, {
test: /\.less$/i,
loader: [
// compiles Less to CSS
"style-loader",
"css-loader",
"less-loader",
],
}, {
test: /\.(png|jpg|gif)$/i,
use: [{
loader: 'url-loader',
options: {
//当加载的图片小于limit时,会将图片编译成base64位字符串形式
//当加载的图片大于limit时,需要使用file-loader模块进行加载
limit: 8192,
},
}, ],
}
],
},
}
04day
01.(掌握)webpack-less文件的打包less-loader
- 1.预处理:写*.less文件,引入到main.js(目的是打包less格式的样式文件)
- 2, npm install --save-dev less-loader@4.1.0 less@3.9.0
- 3,配置webpack.config.js(详见03-16)
- 4.npm run build
02.(掌握)webpack图片文件的打包url-loader
- 1.预处理:css加背景图,引入到main.js
- 2.npm install --save-dev url-loader@1.1.2 (图片小于limit尺寸)或者npm install file-loader@3.0.1 --save-dev(图片大于limit)
- 3,配置webpack.config.js(详见03-16),output中添加publicPath: '../dist/' ,涉及到url的东西,都会再前边拼接dist/
- 4.npm run build
03.(掌握)webpack ES6转ES5的babel-loader
- 需求:由于打包后的文件还是ES6,并没有转成ES5,有些浏览器并不能运行,所以用babel-loader
- 1.npm install --save-dev babel-loader@7 babel-core babel-preset-es2015 ; 2.配置webpack.config.js:
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
04.(掌握)webpack-使用vue的配置过程
- import {name,age} from './js/info.js'有路径,而import Vue from 'vue',没写路径,就从node_modules library root里边拿;
- npm install vue --save 不需要加-dev因为加dev是开发时依赖,而vue时整个项目就要依赖;
- 项目里直接使用vue的过程:
1.npm install vue --save
2.webpack.config.js配置:
resolve:{
//alias别名
alias:{
'vue$':'vue/dist/vue.esm.js' //这个指定的有compiler,默认用的是vue.runtime.js
}
}
3.npm install vue-loader@13.0.0 vue-template-compiler@2.5.21 --save-dev
4.开始使用new Vue({})
4,npm run build
05.(掌握)创建Vue时template和el的关系
- 实际开发时index.html里只写 里边不写内容;
- 而如main.js中的new Vue({el..template})如果同时存在el和template,会自动用template内容替换
06.(掌握)Vue的终极使用方法
- 第一次抽离,index.html只保留 ,在main.js中写组件:
import Vue from 'vue';
// import { Cpn } from './vue/app.js'
const Cpn = {
template:`
<div>
<h2>{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
</div>
`,
data(){
return{
message:'hello world',
name:'coderwhy'
}
},
methods:{
btnClick(){
console.log("btnClick");
}
}
};
- 第二次抽离,const Cpn对象全部抽到app.js中。下边这个Vue是一个根组件,运行只会template会替换el,new Vue({ el:'#app'})之前的组件都是再 里使用,由于现在index.html不让写东西里,就写到这里用,new Vue本身也是一个根组件,只要是组件都有template属性。
//app.js
import Vue from 'vue';
import { Cpn } from './vue/app.js'
//下边这个Vue是一个根组件,运行只会template会替换el
new Vue({
el:'#app',
template:'<Cpn/>',
components:{
Cpn
}
})
- 第三次抽离,app.js内容全部抽到App.vue中,main.js导入App.js模块实现template-script-style三者相分离:
//App.vue
<template>
<div>
<h2 class="title">{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
</div>
</template>
<script>
export default {
name:Cpn,
data(){
return{
message:'hello world',
name:'coderwhy'
}
},
methods:{
btnClick(){
console.log("btnClick");
}
}
}
</script>
<style scoped>
.title{
color: green;
}
</style>
- import Cpn from './Cpn.vue' 的.vue想省略的话,需要需改webpack.config.js:resolve:{extensions:['.js' , '.css' , '.vue']}
07.(掌握)webpack-横幅Plugin的使用-版权声明
- plugin是插件的意思,通常是用于对某个现有的架构进行扩展。 webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等。
- loader和plugin区别 loader主要用于转换某些类型的模块,它是一个转换器。 plugin是插件,它是对webpack本身的扩展,是一个扩展器。
- plugin的使用过程:
- 步骤一:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
- 步骤二:在webpack.config.js中的plugins中配置插件。
- 添加版权:const webpack = require('webpack'); module.exports = {plugins:[new webpack.BannerPlugin('该版权归龙猫所有!')]}
08.(掌握)webpack-HtmlWebpackPlugin的使用
- HTML打包目的是为了让index.html打包到dist文件夹中,过程:
1.安装 npm install html-webpack-plugin --save-dev
2. 配置webpack.config.js:
const HtmlWebpackPlugin = require('html-webpack-plugin');
new HtmlWebpackPlugin({template:'index.html'}),加了参数后,为了让原index.html的
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
// publicPath: '../dist/' //涉及到url的东西,都会再前边拼接dist/
},
module: {
rules: [{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
{
test: /\.less$/i,
loader: [
// compiles Less to CSS
"style-loader",
"css-loader",
"less-loader",
],
}, {
test: /\.(png|jpg|gif)$/i,
use: [{
loader: 'url-loader',
options: {
//当加载的图片小于limit时,会将图片编译成base64位字符串形式
//当加载的图片大于limit时,需要使用file-loader模块进行加载
limit: 8192,
},
}, ],
},
{
test: /\.js$/,
// exclude: 排除
// include: 包含
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
},
{
test: /\.vue$/,
use: ['vue-loader']
}
],
},
resolve: {
// alias: 别名
extensions: ['.js', '.css', '.vue'],
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
plugins: [
new webpack.BannerPlugin('版权最终归龙猫所有'),
new HtmlWebpackPlugin({
template: 'index.html' //为了让原index.html的<div id="app"><div>引进进来
})
]
}
09.(掌握)webpack-UglifyjsWebpackPlugin的使用(压缩js文件)
- npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
- const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
- plugins: [ new UglifyjsWebpackPlugin() ]
10.(掌握)webpack-dev-server搭建本地服务器(npm run dev)
过程:
1.npm install --save-dev webpack-dev-server@2.9.3
2.配置webpack.config.js:
devServer:{
contentBase:'./dist',
inline:true,
}
3.配置package.js的script:"dev":"webpack-dev-server",或者script:"dev":"webpack-dev-server --open"默认打开
4.运行npm run dev
11.(掌握)webpack配置文件的分离
- 需求:开发时和发布时依赖的配置文件不一样,后边脚手架用法哦了,提前了解
- 分离过程:
- 1.把webpack.config.js分离成base.config.js、prod.config.js、base.config.js
- 2.npm install webpack-merge@4.1.5 --save-dev
- 3.package.json配置:"build": "webpack --config ./build/prod.config.js" , "dev": "webpack-dev-server --open --config ./build/dev.config.js"
发布时base.config.js+prod.config.js => npm run build
开发时base.config.js+dev.config.js => npm run dev
//prod.config.js
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig,{
plugins:[
new UglifyjsWebpackPlugin()
]
})
//dev.config.js
const webpackMerge = require('webpack-merge')
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig,{
devServer:{
contentBase:'./dist',
inline:true,
}
})