系统学习前端Vue框架,笔记记录于B站的why老师,具体视频链接,在此感谢老师的悉心授课。我的github笔记地址

欢迎访问我的博客

My Blog: https://coderblue.cn/
Github:https://github.com/CoderBleu
My Project:https://coderblue.cn/project/

初始化Vue

初始Vue

<script src="../js/vue.js"></script><div id="hi">Hello {{name}}</div><div class="movie"><ul><li v-for="item in movies">{{item}}</li></ul></div><script>const hi = new Vue({el: "#hi",data: {name: 'Vue.js'}})let movie = new Vue({el: '.movie',data: {movies: ["星际穿越", '大话西游', '海贼王之黄金城', '复仇者联盟']// 注意:可以通过movie.movies.push('盗梦空间')}})</script>

简易计数器

<body>
<!-- view -->
<div id="count"><h2>{{counter}}</h2><button v-on:click="add">+</button><button v-on:click="sub">-</button><!-- <button v-on:click="counter++">+</button><button v-on:click="counter--">-</button> -->
</div>
<ol><li>原因是你的body中的div中没有设置id,vue没有绑定</li><li>解决:body中加 div id="app" </li><li>双向绑定:view,model,ViewModel</li>
</ol>
</body>
<script>// proxy: modelconst obj = {counter: 0}// ViewModellet count = new Vue({el: "#count",data: obj,methods: {add: function() {this.counter++;},sub: function(){this.counter--;}}})
</script>

Vue中的MVVM

MVVM简介
  MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,其核心是提供对View 和 ViewModel 的双向数据绑定,这使得ViewModel 的状态改变可以自动传递给 View,即所谓的数据双向绑定。
  Vue.js 是一个提供了 MVVM 风格的双向数据绑定的 Javascript 库,专注于View 层。它的核心是 MVVM 中的 VM,也就是 ViewModel。 ViewModel负责连接 View 和 Model,保证视图和数据的一致性,这种轻量级的架构让前端开发更加高效、便捷。

MVVM模型

MVVM拆开来即为Model-View-ViewModel,有View,ViewModel,Model三部分组成。

  • View层:是视图、模版,负责将数据模型转化为UI展现出来。
  • Model层:是模型、数据,可以在Model层中定义数据修改和操作的业务逻辑。
  • ViewModel层:View层和Model层并没有直接联系,而是通过ViewModel层进行交互。ViewModel层通过双向数据绑定将View层和Model层连接了起来,使得View层和Model层的同步工作完全是自动的。

Vue.js中mvvm的体现
  Vue.js的实现方式,对数据(Model)进行劫持,当数据变动时,数据会出发劫持时绑定的方法,对视图进行更新。

实例分析如下:

Vue.js关于双向数据绑定的一些实现细节

  vue是采用Object.defineProperty的getter和setter,并结合观察者模式来实现数据绑定的。当把一个普通的javascript对象传给Vue实例来作为它的data选项时,Vue将遍历它的属性,用Object.defineProperty将它们转为getter/setter。用户看不到getter/setter,但是在内部它们让Vue追踪依赖。在属性被访问和修改时通知变化。

  • Observer相当于Model层观察vue实例中的data数据,当数据发生变化时,通知Watcher订阅者。
  • Compile指令解析器位于View层,初始化View的视图,将数据变化与更新函数绑定,传给Watcher订阅者。
  • Watcher是整个模型的核心,对应ViewModel层,连接Observer和Compile。所有的Watchers存于Dep订阅器中,Watcher将Observer监听到的数据变化对应相应的回调函数,处理数据,反馈给View层更新界面视图。
  • Dep消息订阅器,内部维护了一个数组,用来收集订阅者(watcher),数据变动触发notify函数,再调用订阅者的update方法。

基本模板语法

<!DOCTYPE html>
<html lang="zh-CN">
<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"><title>Document</title><script src="../js/vue.js"></script><style>[v-cloak] {display: none !important;}</style>
</head><body><div id="app"><!-- 让未被编译的重新编译加载 Vue加入了延缓响应的指令v-cloak,在与css:[v-cloak] { display: none } 的配合下,可以隐藏未编译 Mustache 的标签直到实例准备完毕,v-cloak属性才会被自动去除,对应的标签也才可见了--><h4 >Hello {{count}}</h4></div><script>setTimeout(() => {let app = new Vue({el: '#app',data: {count: 'v-cloak'},methods: {},beforeMount () {alert("有趣");}});}, 1000);</script>
</body></html>

动态绑定属性

class的绑定

传给 v-bind:class 一个对象,以动态地切换 class(语法糖:’:表示’)

根据isActive的true,false变化,动态绑定单个class

<div :class="{ active: isActive==true }"></div>

计算属性的方式绑定class

<div :class="classObject"></div>
data: {isActive: true,error: null
},
computed: {classObject: function () {return {active: this.isActive && !this.error,  //isActive为true,且error不为null'text-danger': this.error && this.error.type === 'fatal'//error为null且this.error.type === 'fatal'}}
}

数组的方式绑定class

<div v-bind:class="[activeClass, errorClass]"></div>
data: {activeClass: 'active',errorClass: 'text-danger'
}
渲染为:
<div class="active text-danger"></div>

三元表达式动态切换class(推荐)

<div :class="[isActive ? activeClass : '', errorClass]"></div>

style的绑定

v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来) 来命名:

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {activeColor: 'red',fontSize: 30
}

直接绑定到一个样式对象通常更好,这会让模板更清晰:

<div :style="styleObject"></div>
data: {styleObject: {color: 'red',fontSize: '13px'}
}

v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:

<div v-bind:style="[baseStyles, overridingStyles]"></div>

计算属性

<body><div id="app"><h3> {{fullName}}</h3></div><script>let app = new Vue({el: '#app',data: {fisrtName: 'Lom',lastName: 'Name'},computed: {// fullName: function(){//     return this.fisrtName + ' ' + this.lastName;// }// fullName: {//   get: function () {//     return this.fisrtName + ' ' + this.lastName;//   }// }// 底层方法fullName: {set: function (newValue) {console.log("---------", newValue);//split() 方法用于把一个字符串分割成字符串数组。const value = newValue.split(' ');this.fisrtName = value[0];this.lastName = value[1];},get: function () {return this.fisrtName + ' ' + this.lastName;}}}});</script>
</body>

计算属性和methods的对比

<body><div id="app"><!-- 1. 通过字符串直接拼接,不美观 --><h3> {{fisrtName}} {{lastName}}</h3><!-- 2. 通过方法:调用方法一次就重新执行一次 --><h3> {{getFullName()}}</h3><!-- 3. 通过计算属性:执行一次后,会将此缓存起来,后面在调用会直接有结果显示,不会频繁调用 --><h3> {{fullName}}</h3></div><script>const app = new Vue({el: '#app',data: {fisrtName: 'Lom',lastName: 'Name'},methods: {getFullName: function(){console.log("fullName");return this.fisrtName + ' ' + this.lastName;}},computed: {fullName: {get: function () {console.log("fullName");return this.fisrtName + ' ' + this.lastName;}}}});</script>
</body>

事件监听

v-on的基本使用

<body><div id="app"><h3>{{counter}}</h3><!-- v-on语法糖使用:@ --><button @click="increment">+</button><!-- 不需要传参数,故函数的双括号可以省略 --><button v-on:click="decrement()">-</button></div><script>let app = new Vue({el: '#app',data: {counter: 0},methods: {increment(){this.counter++},decrement(){this.counter--}}});</script>
</body>

v-on的参数问题

<body><div id="app"><!-- 不需要传参数,故函数的双括号可以省略 --><button @click="btn0Click">+</button><!-- 不传参数,会显示event对象 --><button @click="btn1Click()">+</button><!-- 带括号传参数 --><button @click="btn2Click(value)">+</button><!-- 定义方法时,我们既需要传参数,又需要获取到event对象,可以通过$event获得event对象 --><button @click="btn3Click(value, $event)">+</button></div><script>let app = new Vue({el: '#app',data: {value: 13},methods: {btn0Click() {console.log("btn1Click");},btn1Click() {console.log("======", event);},btn2Click(value) {console.log("------", value);},btn3Click(value, event) {console.log("+++++", value, event);}},});</script>
</body>

v-on的修饰符

<body><div id="app"><!-- 1. .stop修饰符的使用:阻止单击事件继续传播给后续定义的函数 --><div @click="divClick"><button @click.stop="btnClick">点击</button></div><!-- 2. .prevent修饰符的使用:阻止事件的自带默认行为, --><form action=""><input type="submit" value="提交" @click.prevent="submitClick"></button></form><!-- 3. .keyup修饰符的使用:当键盘被释放时触发事件 --><input @keyup="keyup">松开</input><br><input @click.keyup="keyup">鼠标点击松开</input><br><!-- 3. .enter修饰符的使用:当键盘按下回车键触发事件 --><input @keyup.enter="enter">回车</input><br><!-- 4. .once修饰符的使用: 只能触发一次回调 --><button @click.once="once">只能点击一次</button></div><script>let app = new Vue({el: '#app',data: {},methods: {btnClick(){console.log("btnClick")},divClick(){console.log("divClick")},submitClick(){console.log("submitClick")},keyup(){console.log("keyup")},enter(){console.log("enter")},once(){console.log("once")}}});</script>
</body>

条件判断

v-if的基本使用

<body><div id="app"><!-- 从控制台输入 app.isShow = false就可以将其隐藏 --><h2 v-if="isShow"><div>abc</div><div>abc</div><div>abc</div><div>abc</div><div>abc</div>{{message}}</h2></div><script>let app = new Vue({el: '#app',data: {message: '哈哈',isShow: true},methods: {}});</script>
</body>

v-if和v-else的使用

<body><div id="app"><!-- 从控制台输入 app.isShow = false就可以将其隐藏 --><h2 v-if="isShow"><div>abc</div><div>abc</div><div>abc</div><div>abc</div><div>abc</div>{{message}}</h2><h2 v-else>当v-if为false的时候,我就开始显示了,我和v-if要紧连着使用</h2></div><script>let app = new Vue({el: '#app',data: {message: '哈哈',isShow: true},methods: {}});</script>
</body>

v-if和v-else-if和v-else的使用

<body><div id="app"><p v-if="score >= 90">优秀</p><p v-else-if="score >= 80">良好</p><p v-else-if="score >= 60">及格</p><p v-else>不及格</p></div><script>let app = new Vue({el: '#app',data: {score: 99}});</script>
</body>

用户登录切换的案例

<body><div id="app"><span v-if="isShowUserName"><label for="userName">用户名</label><input type="text" id="userName" placeholder="请输入用户名"></span><span v-else><label for="email">用户邮箱</label><input type="text" id="email" placeholder="请输入用户邮箱"></span><button @click="isShowUser">切换类型1</button><button @click="isShowUserName = !isShowUserName">切换类型2</button></div><script>let app = new Vue({el: '#app',data: {isShowUserName: true},methods: {isShowUser() {this.isShowUserName = !this.isShowUserName}}});</script>
</body>

用户登录切换的案例(复用的小问题)

<body><div id="app"><!-- 注意:Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用先前的input框,而不是重新创建元素如若我们不需要复用input框,只需要添加一个 key就好,相同的key值才会复用--><span v-if="isShowUserName"><!-- label中的for指向input后,点击label可以将光标聚焦给input框 --><label for="userName">用户名</label><input type="text" id="userName" placeholder="请输入用户名" key="userName"></span><span v-else><label for="email">用户邮箱</label><input type="text" id="email" placeholder="请输入用户邮箱" key="userName"></span><button @click="isShowUser">切换类型1</button></div><script>let app = new Vue({el: '#app',data: {isShowUserName: true},methods: {isShowUser() {this.isShowUserName = !this.isShowUserName,//第二种方式:使用js将input框值清空document.getElementById("userName").value = '';document.getElementById("email").value = '';}}});</script>
</body>

v-show的使用

<body><div id="app"><!-- v-if 和 v-show的区别:v-if为false的时候,压根不会存在dom里面,v-show为false的时候,只是会增加个行内样式:display:none;建议:如果需要频繁切换的时候,使用v-show,只有一次切换时通常使用v-if--><span v-if="isShow" id="isShow">V-if</span><span v-show="isShow" id="VShow">V-show</span></div><script>const app = new Vue({el: '#app',data: {isShow: true},methods: {}});</script>
</body>

循环遍历

v-for遍历数组

<body><div id="app"><!-- 1.简单遍历数组 --><ul><li v-for="item in movies">{{item}}</li></ul><br><!-- 2.带索引遍历数组 --><ul><li v-for="(item, index) in movies">{{index + 1}}.{{item}}</li></ul><br><!-- 3.带索引遍历数组,且添加监听事件 --><ul><li v-for="(item, index) in movies" @click="showIndex(index)">{{index + 1}}.{{item}}</li></ul></div><script>const app = new Vue({el: '#app',data: {movies: ['海王', '大话西游', '星际争霸', '三傻大闹宝莱坞']},methods: {showIndex(index) {console.log('第' + (index + 1) + '个值');}}});</script>
</body>

v-for遍历对象

<body><div id="app"><!-- 1.在遍历对象的过程中,如果只是获得一个值,那么获取到的就是整个对象的value值 --><ul><li v-for="item in obj">{{item}}</li></ul><br><!-- 2.在遍历对象的同时附带格式:value和key (value, key) --><ul><li v-for="(item, key) in obj">{{item}}--{{key}}</li></ul><br><!-- 3.在遍历对象的同时附带格式:value和key和index (value, key, index) --><ul><li v-for="(value, key, index) in obj">{{value}}--{{key}}--{{index+1}}</li></ul><br></div><script>const app = new Vue({el: '#app',data: {obj: {id: 12,name: 'Luck',height: 1.78}},methods: {}});</script>
</body>

v-for使用过程添加key

<body><!-- splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。1.app.obj.splice(4) 去除数组第五个值2.app.obj.splice(3,0,'F') 在数组第四个值的时候添加F--><div id="app"><ul><!-- 不添加key属性: 在数组中间插入数据,需要将插入位置后面的值都后移,效率慢 --><!-- 添加key属性:类似于链表一样,我插入中间只需要将两端值的指向指给我,效率高--><li v-for="item in obj" :key="item">{{item}}</li></ul></div><script>const app = new Vue({el: '#app',data: {obj: ['A', 'B', 'C', 'D', 'E']},methods: {}});</script>
</body>

哪些数组的方法是响应式的

<body><div id="app"><ul><li v-for="item in obj" :key="item">{{item}}</li></ul><button @click="btnClick">点击</button></div><script>let myDate = new Date();const app = new Vue({el: '#app',data: {obj: ['R', 'C', 'B', 'D', 'E'],},methods: {btnClick() {//1.push()方法// this.obj.push('F')//2.pop()方法,从末尾依次删除// this.obj.pop()//3.shift()方法,从头开始依次删除// this.obj.shift()//4.unshift(),从头开始依次添加元素// this.obj.unshift('G','L')//5.splice(),方法向/从数组中添加/删除/替换元素,然后返回被删除的项目。//this.obj.splice(4) //去除数组第五个值//6.sort(),对数组的元素进行排序// this.obj.sort()//7.reverse(),对数组进行反转// this.obj.reverse()//注意:通过索引值修改数组中的元素方法,不可取,vue不会帮我渲染修改后的值// this.obj[0] = 'M'//解决方法://删除第五个// this.obj.splice(3, 4, 'N')//①:在第三个位置修改成 N// this.obj.splice(2, 1, 'N')//②:建议:Vue自带的修改方法Vue.set(this.obj, 2, 'G')}}});</script>
</body>

对遍历出的列表,点击后对应的能变红

<body><div id="app"><ul><!-- 对遍历出来的结果列表,我能实现点击列表对应的值,能让它变成红色 --><li v-for="(item, index) in obj" :key="item":class="{active: currentIndex === index}" @click="changeColor(index)">{{item}}</li></ul></div><script>let myDate = new Date();const app = new Vue({el: '#app',data: {obj: ['R', 'C', 'B', 'D', 'E'],currentIndex: 0},methods: {changeColor(index) {this.currentIndex = index;}}});</script>
</body>

书籍购物车案例

此案例包含JavaScript的高阶函数用法

// javascript的高阶函数:
const nums = [10, 20, 30, 40, 50];// 编程式范式:命令式编程/声明式编程
// 编程式编程(第一公民:对象),函数式编程(第一公民:函数)
// filter/map/reduce高阶函数
// ①:
// filter高阶函数的使用: 它的回调函数有一个要求,必须返回一个boolean值
// true:当返回true时,函数内部会自动将这次回调的n加入到新的数组中去
// false:当返回false时,函数内部会过滤掉这次的 n
let newNum1 = nums.filter(function (n) {return n < 100;
})
console.log('newNum1==filter==' + newNum1);// ②:
// map高阶函数的使用
let newNum2 = newNum1.map(function (n) {return n * 2;
})
console.log('newNum2==map==' + newNum2);
let newNum21 = nums.map(function (n) {// 判断条件无效,输出结果与上面一样,看来还是需要filter来过滤if (n < 80) {return n * 2;} else {return;}
})
console.log('newNum21==map==' + newNum21);// ③:
//6.reduce高阶函数的使用: 对数组中的所有数据进行汇总(相加,相乘......)
let total = newNum2.reduce(function (preValue, value) {return preValue + value;
}, 0)
console.log('total===' + total);
// 长度为 5
//         preValue   value
//第一次:    0         20
//第二次:    20        40
//第三次:    60        60
//第四次:    120       80
//第五次:    200       100
//输出        300//④:将上面三个函数综合起来使用:
let sum = nums.filter(function (n) {return n < 50
}).map(function (n) {return n * 2
}).reduce(function (preValue, value) {return preValue + value
}, 0)
console.log('sum===' + sum);//⑤:使用箭头函数将上面三个函数综合起来使用(类似lombda表达式)
let sum1 = nums.filter(n => n < 50).map(n => n * 2).reduce((pre, value) => pre + value);
console.log('sum1===' + sum1);

index.html

<!DOCTYPE html>
<html lang="en"><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"><title>Document</title><link rel="stylesheet" href="style.css">
</head><body><div id="app"><div v-if="isFull"><table><thead><tr><th>编号</th><th>书名</th><th>出版日期</th><th>价格</th><th>数量</th><th>操作</th></tr></thead><tbody><tr v-for="(item,index) in books" :class="{changeColor: number == 1}" @mouseenter="change(index)"@mouseleave="remove(index)"><td>{{item.id}}</td><td>{{item.name}}</td><td>{{item.date}}</td><!-- 使用函数:<td>{{getfinalPrice(item.price)}}</td> --><!-- 使用过滤器: --><td>{{item.price | showPrice}}</td><td><button @click="subBtn(index)">-</button>{{item.count}}<button @click="addBtn(index)">+</button></td><td><button v-if="exchange" @click="addItemBtn(index)">新增</button><button v-else @click="removeBtn(index)">移除</button><button @click="changeType()">切换类型</button></td></tr></tbody></table><br><span>总价格:{{showTotalPrice | showPrice}}</span></div><h2 v-else>购物车清空</h2></div>
</body>
<script src="../js/vue.js"></script>
<script src="main.js"></script></html>

main.js

const app = new Vue({el: '#app',data: {books: [{id: 1,name: '算法导论',date: '2019-2',price: 87.21,count: 1,exchange: true},{id: 2,name: 'UNIX编程艺术',date: '2019-4',price: 97.21,count: 2,exchange: true},{id: 3,name: '编程珠玑',date: '2012-2',price: 77.21,count: 1,exchange: true},{id: 4,name: '大话西游',date: '2019-7',price: 100,count: 1,exchange: true}],number: 2,exchange: false,isFull: true},computed: {showTotalPrice() {let totalPrice = 0;//1.普通for循环// for (let i = 0; i < this.books.length; i++) {//   totalPrice += this.books[i].price * this.books[i].count// }// return totalPrice//2.index是索引// for (let index in this.books) {//   totalPrice += this.books[index].price * this.books[index].count// }//3.for of// for (let item of this.books) {//   totalPrice += item.price * item.count// }//4.利用reduce函数来写return this.books.reduce(function (preValue, book) {return preValue + book.price * book.count}, 0)return totalPrice}},methods: {// 行内按钮操作subBtn(index) {if (this.books[index].count > 0) {this.books[index].count--}},addBtn(index) {this.books[index].count++},removeBtn(index) {this.books.splice(index, 1)if (this.books.length <= 0) {this.isFull = !this.isFull}},// 鼠标移动进区域,改变背景颜色change(index) {// thisthis.number = 1;this.active = ".changeColor{ background-color: #cae6e6}"},remove(index) {this.number = 2},// 改变按钮类型changeType() {this.exchange = !this.exchange},addItemBtn() {const obj = [5, '数值分析', '2018-8', 96.10, 2];this.books.push(obj)},// 格式化价格getfinalPrice(price) {return '¥' + price.toFixed(2);}},filters: {showPrice(price) {//.toFixed(2):保留小数点后两位return '¥' + price.toFixed(2);}}
})

style.css

table {border: 1px solid #cccccc;/* 消除表格的边框内距 */border-collapse: collapse;border-spacing: 0;width: 700px;
}
table thead {background-color: lightskyblue;
}
/* table tr:hover {background-color: pink;
} */
table tr th {border: 1px solid #cccccc;
}
table tr td {border: 1px solid #cccccc;text-align: center;padding: 20px;
}
.changeColor {background-color: #cae6e6
}

v-model的使用

v-model双向绑定的基本原理

<body><div id="app"><!-- v-model的基本使用 --><input type="text" v-model="message">{{message}}<br><!-- v-model的原理: --><!-- 监听input内容改变事件 --><input type="text" v-on:input="changeInput($event)"><input type="text" @input="changeInput"><br><!-- 此方法:input中会直接有message的值 , 其中target是事件源--><input type="text" v-bind:value="message" v-on:input="message = $event.target.value"><input type="text" :value="message" @input="message = $event.target.value"></div><script>const app = new Vue({el: '#app',data: {message: '你好呀'},methods: {changeInput(event) {this.message = event.target.value;}}});</script>
</body>

v-model结合radio单选框使用

<body><div id="app"><!-- name要一致,不然选择一个单选框,再选择另一个,之前那个还是被选中状态 --><label for="male"><input type="radio" name="sex" value="男" id="male" v-model="message">男</label><label for="female"><input type="radio" name="sex" value="女" id="female" v-model="message">女</label><br>{{'你选中的值:' + message}}</div><script>const app = new Vue({el: '#app',data: {message: ''},methods: {}});</script>
</body>

v-model结合checkbox多选框使用

<body><div id="app"><!-- 单个多选框: 同意协议示范 --><label for="agreeLisence"><input type="checkbox" v-model="isAgree">统一协议</label><button :disabled="!isAgree">下一步</button><br><!-- 多个多选框:爱好 --><input type="checkbox" value="唱" v-model="hobbies">唱<input type="checkbox" value="跳" v-model="hobbies">跳<input type="checkbox" value="rap" v-model="hobbies">rap<input type="checkbox" value="打篮球" v-model="hobbies">打篮球你选择的兴趣爱好是:{{hobbies}}</div><script>const app = new Vue({el: '#app',data: {isAgree: false, //单选框hobbies: [] //多选框},methods: {}});</script>
</body>

v-model结合select下拉框使用

<body><div id="app"><!-- 下拉框的单个使用 --><select name="demo" v-model="fruit"><option value="香蕉">香蕉</option><option value="苹果">苹果</option><option value="葡萄">葡萄</option><option value="梨子">梨子</option></select><h3>你选择的水果是:{{fruit}}</h3><br><!-- 下拉框的多个选中使用: 注意添加multiple,然后选择多个需要按住ctrl键 --><select name="demo" v-model="fruits" multiple><option value="香蕉">香蕉</option><option value="苹果">苹果</option><option value="葡萄">葡萄</option><option value="梨子">梨子</option></select><h3>你选择的水果是:{{fruits}}</h3></div><script>const app = new Vue({el: '#app',data: {fruit: '香蕉',fruits: []},methods: {}});</script>
</body>

v-model结合v-for使用

<body><div id="app"><!-- 单个多选框: 同意协议示范 --><label v-for="(item, index) in originalHobbies"><!-- 如果绑定originalHobbies,点击下对应的多选框就会消失 --><input type="checkbox" v-model="hobbies" :id="index+1" :value="item">{{item}}</label>你选择的兴趣爱好是:{{hobbies}}</div><script>const app = new Vue({el: '#app',data: {isAgree: false, //单选框hobbies: [], //多选框originalHobbies: ['唱', '跳', 'rap', '打篮球']},methods: {}});</script>
</body>

v-model的修饰符使用

<body><div id="app"><!-- 1.lazy修饰符: 懒加载,可以让数据被按下回车失去焦点后才会更新 --><input type="text" v-model.lazy="message">{{message}}<hr><!-- 2.number修饰符:可以将 只能输入数字 的类型转换成String --><input type="number" v-model="age"> <h3>{{age}}--{{typeof age}}</h3><!-- 如果不想转换成String类型,只要添加 .number --><input type="number" v-model.number="height"> <h3>{{height}}--{{typeof height}}</h3><hr><!-- 3.去掉两端的空格 --><input type="text" v-model.trim="name"> <!-- 添加多个修饰符只需叠加后面就行,无先后顺序要求 --><input type="number" v-model.lazy.number="height"> <h3>{{name}}</h3></div><script>const app = new Vue({el: '#app',data: {message: '你好呀',age: 0,height: 1,name: ''},methods: {}});</script>
</body>

组件化开发

组件化的基本使用

<body><div id="app"><my-cpn></my-cpn><my-cpn></my-cpn><my-cpn></my-cpn><my-cpn></my-cpn></div><script>// 创建组件构造器const cpnC = Vue.extend({// ES6语法:` 号可以支持内容里面换行比 ''更好使用//如果有多个标签使用,必须有个div包裹起来,否则内容显示不完全template: `<div><h2>组件化</h2><h3>我是,哈哈哈哈</h3><h3>我是,呵呵呵呵</h3></div>`})// 注册组件Vue.component('my-cpn', cpnC)const app = new Vue({el: '#app',data: {},methods: {}});</script>
</body>

全局组件和局部组件

<body><div id="app"><cpn></cpn><cpn></cpn><cpn></cpn><cpn></cpn></div><div id="app2"><cpn></cpn></div><script>const cpnC = Vue.extend({template: `<div><h2>组件化</h2><h3>我是,哈哈哈哈</h3><h3>我是,呵呵呵呵</h3></div>`})// 注册全局组件Vue.component("cpn", cpnC)const app = new Vue({el: '#app',data: {},components: {// 注册局部组件,即只能在app里使用cpn这个组件cpn: cpnC}});const app2 = new Vue({el: '#app2',});</script>
</body>

父组件和子组件

<body><div id="app"><cpn2></cpn2></div><script>const cpnC = Vue.extend({template: `<div><h2>子组件</h2><h3>我是,哈哈哈哈</h3><h3>我是,呵呵呵呵</h3></div>`})// 父组件:root组件const cpnC2 = Vue.extend({template: `<div><h2>父组件</h2><h3>我是,哈哈哈哈</h3><h3>我是,呵呵呵呵</h3>// 这个子组件需要先注册<cpn1><cpn1/></div>`,components: {cpn1: cpnC}})// 注册全局组件Vue.component("cpn", cpnC)const app = new Vue({el: '#app',data: {},components: {// 注册局部组件,即只能在app里使用这个组件cpn1: cpnC,cpn2: cpnC2}});</script>
</body>

组件的语法糖注册方式

<body><div id="app"><cpn1></cpn1><cpn2></cpn2></div><script>// const cpnC = Vue.extend()// 语法糖注册全局组件Vue.component("cpn1", {template: `<div><h2>我是cpn1</h2><h3>我是,哈哈哈哈</h3></div>`})const app = new Vue({el: '#app',data: {},components: {// 语法糖注册局部组件,即只能在app里使用这个组件'cpn2': {template: `<div><h2>我是cpn2</h2><h3>我是,呵呵呵呵</h3></div>`}}});</script>
</body>

组件模块的分离写法

<body><div id="app"><cpn></cpn><cpn1></cpn1></div><!-- 1.使用script标签:注意类型需要添加:text/x-template --><script type="text/x-template" id="cpn"><div><h2>我是cpn1</h2><h3>我是,哈哈哈哈</h3></div></script><!-- 2.使用template标签(推荐) --><template id="cpn1"><div><h2>我是cpn1</h2><h3>我是,哈哈哈哈</h3></div></template><script>// const cpnC = Vue.extend()// 语法糖注册全局组件Vue.component("cpn", {// '#cpn' template: '#cpn1'})const app = new Vue({el: '#app',data: {}});</script>
</body>

组件中的数据存放问题

<body><div id="app"><cpn></cpn><cpn></cpn></div><template id="cpn1"><div><h2>我是cpn1</h2><h3>我是,哈哈哈哈</h3><!-- 想要获取title,必须在组件里面定义一个函数,且有返回值 --><h3>{{title}}</h3></div></template><script>// const cpnC = Vue.extend()// 语法糖注册全局组件Vue.component("cpn", {// '#cpn' template: '#cpn1',data() {return {title: '好好学习,天天向上'}}})const app = new Vue({el: '#app',data: {// 模板里的title不能获取到此值title: '好好学习,天天向上'}});</script>
</body>

组件中的data为什么必须是函数

<body><div id="app"><cpn></cpn><cpn></cpn><hr><cpn1></cpn1><cpn1></cpn1></div><template id="cpn1"><div><h3>当前计数:{{count}}</h3><button @click="increment">+</button><button @click="decrement">-</button></div></template><template id="cpn2"><div><h3>当前计数:{{count}}</h3><button @click="increment">+</button><button @click="decrement">-</button></div></template><script>// 推荐:count数据不会共享// 使用data函数:不会引起连锁反应。即每个都是个新对象,值地址不一样,Vue.component("cpn", {// '#cpn' template: '#cpn1',data() {return {count: 0}},methods: {increment() {this.count++},decrement() {this.count--}}})// count数据共享// 都是使用的这个obj常量const obj = {count: 0};Vue.component("cpn1", {// '#cpn' template: '#cpn2',data() {return obj},methods: {increment() {this.count++},decrement() {this.count--}}})const app = new Vue({el: '#app',});</script>
</body>

组件通信-父组件向子组件传递数据

<body><div id="app">{{movies.toString()}}<hr><!-- 添加了前缀v-bind,vue会帮我们解析movies,不会当成字符串处理 --><cpn v-bind:vmoives="movies" :vmessage="message"></cpn><hr><!-- 当成字符串处理 --><cpn vmoives="movies" vmessage="message"></cpn></div><template id="cpn"><div><h2>{{vmessage}}</h2><ul v-for="(item,index) in vmoives"><li>{{index}}.{{item}}</li></ul></div></template><script>const cpn = {template: '#cpn',props: ['vmoives', 'vmessage'], //这种其实是表示变量名字,不能当成字符串data() {return {}},methods: {}}// 注册全局组件// Vue.component('cpn', cpn)const app = new Vue({el: '#app',data: {movies: ['海王', '海贼王', '航空母舰'],message: '真香'},components: {// ES6中的高阶写法,等同于 cpn: cpncpn}});</script>
</body>

组件通信-props用法详解

<body><div id="app">{{movies.toString()}}<hr><cpn v-bind:propF="movies" :propC="message"></cpn><hr></div><template id="cpn"><div><h2>{{propC}}</h2><ul v-for="(item,index) in propF"><li>{{index}}.{{item}}</li></ul></div></template><script>const cpn = {template: '#cpn',props: {// 基础的类型检查('null'匹配任何类型)propA: Number,// 多个可能的类型propB: [String, Number],// 必填的字符串propC: {type: String,required: true,default: '你好呀'},// 带有默认值的数字propD: {type: Number,default: 100},// 注意:类型是对象/数组时,默认值必须是一个函数// 带有默认值的对象propE: {type: Object,default: function () {return {message: 'hello'}}},// 带有默认值的对象propF: {type: Array,default() {return ['大话西游', '造梦西游']}},// 自定义验证函数propG: {validator: function (value) {// 这个值必须匹配下列字符串的一个return ['success', 'warning', 'danger'].indexOf(value) !== -1}}},data() {return {}}}const app = new Vue({el: '#app',data: {movies: ['海王', '海贼王', '航空母舰'],message: '真香'},components: {cpn}});</script>
</body>

组件通信-父传子(props不支持驼峰标识)

<body><div id="app"><cpn v-bind:prop-f="movies" v-bind:prop-g="message"></cpn><hr></div><template id="cpn"><div><h2>{{propG}}</h2><ul v-for="(item,index) in propF"><li>{{index}}.{{item}}</li></ul></div></template><script>const cpn = {template: '#cpn',props: {// 带有默认值的对象propE: {type: Object,default: function () {return {message: 'hello'}}},// 带有默认值的对象propF: {type: Array,default() {return ['大话西游', '造梦西游']}},// 自定义验证函数propG: {validator: function (value) {// 这个值必须匹配下列字符串的一个:如果要检索的字符串值没有出现,则该方法返回 -1。console.log(['success', 'warning', 'danger'].indexOf(value) !== -1);// 校验失败:Invalid prop: custom validator check failed for prop "propG".return ['success', 'warning', 'danger'].indexOf(value) !== -1}}},data() {return {}}}const app = new Vue({el: '#app',data: {movies: ['海王', '海贼王', '航空母舰'],message: 'succe'},components: {cpn}});</script>
</body>

组件通信-父子组件通信的案例

<body><div id="app"><!-- cpnClick在父组件中定义的方法 --><cpn v-on:item-click="cpnClick"></cpn><hr><cpn @item-click="cpnClick($event)"></cpn></div><template id="cpn"><div><button v-for="(item,index) in categories" @click="btnClick(item)">{{item.name}}</button></div></template><script>/*步骤:1.子组件:触发监听的事件,比如被点击了,2.然后发送自定义事件this.$emit('cpn中的自定义事件名', item)3.调用Vue中的事件监听函数,如若在html文件中,不饿能使用驼峰命名自定义函数*/// 子组件const cpn = {template: '#cpn',data() {return {categories: [{id: 'a1', name: '热门推荐'},{id: 'a2', name: '手机数码'},{id: 'a3', name: '家用家电'},{id: 'a4', name: '电脑办公'}]}},methods: {btnClick(item) {//发射事件:自定义事件(父组件的cpn中接收此事件的名字)// html不区分大小写,这里不能使用驼峰命名this.$emit('item-click', item)}}}// <!-- 父组件 -->const app = new Vue({el: '#app',components: {cpn},methods: {cpnClick(item) {console.log('cpnClick', item);}},});</script>
</body>

组件通信-数字游戏-1

<body><div id="app"><cpn :number1="num1" :number2="num2"></cpn></div><template id="cpn"><div><h2>双向绑定的是num1:{{dnum1}}</h2><h2>props:{{number1}}</h2><input type="text" v-model="dnum1"><h2>双向绑定的是num2:{{dnum2}}</h2><h2>props:{{number2}}</h2><input type="text"  v-model="dnum2"></div></template><script>const app = new Vue({el: '#app',data: {num1: 0,num2: 1},components: {cpn: {template: '#cpn',props: {number1: Number,number2: Number},data() {return {/* Property or method "num2" is not defined on the instance but referenced during render.Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property即需要添加data(){}*/dnum1: this.number1,dnum2: this.number2}}}},methods: {}});</script>
</body>

组件通信-数字游戏-2

<body><div id="app"><!-- number1的值来源与Vue中data的num1 但是在组件中取值要用{{number1}},也就是props对应的值--><cpn :number1="num1" :number2="num2" @change1props="change1props" @change2props="change2props"></cpn><!-- 3.父组件接收传过来的自定义事件,Vue中的 "change2props"方法 --></div><template id="cpn"><div><h2>双向绑定的是num1:{{dnum1}}</h2><h2>props:{{number1}}</h2><!-- 这方式同 v-model: 监听input框,调用组件中的 changeInputValue1事件 --><input type="text" :value="dnum1" @input="changeInputValue1"><h2>双向绑定的是num2:{{dnum2}}</h2><h2>props:{{number2}}</h2><input type="text" :value="dnum2" @input="changeInputValue2"></div></template><script>const app = new Vue({el: '#app',data: {num1: 0,num2: 1},components: {cpn: {template: '#cpn',props: {number1: Number,number2: Number},data() {return {dnum1: this.number1,dnum2: this.number2}},methods: {changeInputValue1(event) {// 1.将input的值赋值到 dnum1 中去this.dnum1 = event.target.value;// 2.为了让父组件可以修改值,发送一个事件this.$emit('change1props', this.dnum1)// 将下面输入框的props值: 变成1/2this.dnum2 = this.dnum1 / 2this.$emit('change2props', this.dnum2)},changeInputValue2() {this.dnum2 = event.target.value;this.$emit('change2props', this.dnum2)// 将上面输入框的props值: 变成2倍this.dnum1 = this.dnum2 * 2this.$emit('change1props', this.dnum1)}},}},methods: {change1props(value) {// 改变 prop 中的num1的值console.log('dum1' + value);this.num1 = parseFloat(value);},change2props(value) {console.log('dum2' + value);this.num2 = parseFloat(value);}}});</script>
</body>

组件通信-父访问子-children-refs

<body><div id="app"><cpn></cpn><cpn></cpn><cpn ref="refA"></cpn><button @click="btnClick">按钮</button></div><template id="cpn"><div>我是子组件</div></template><script>const app = new Vue({el: '#app',data: {message: 'Lemon'},methods: {btnClick() {// 1. $.chlidrenfor (let item of this.$children) {console.log(item.name);item.showMessage()}// 2. $.refs: 仅仅会调动带有refs标示的// 这样有时候我们想即使在新增数据后,依旧能操控它console.log('refs' + this.$refs.refA.name);}},components: {cpn: {template: '#cpn',data() {return {name: 'Lemon',id: 1,height: 1.78}},methods: {showMessage() {console.log(this.id);}},}}});</script>
</body>

组件通信-子访问父-parent-root

<body><div id="app"><cpn></cpn><hr><ccpn></ccpn></div><template id="cpn"><div>我是cpn子组件</div></template><!-- cpn的子组件 --><template id="ccpn"><div><h3>我是cpn的子组件</h3><button @click="btnClick">ccpn按钮</button></div></template><script>const ccpn = Vue.component('ccpn', {template: '#ccpn',methods: {btnClick() {// 按理这个是cpn的子组件,this应该指ccpn对象,// 调用this.$parent时,访问的是ccpn的父组件cpn,即返回对象是vuecomponents// 调用this.$root时,访问的才应该是cpn的父组件,即返回对象是vue// 可是我放在cpn的components里说ccpn没有注册console.log('ccpn=', this.$root.message)}}})const app = new Vue({el: '#app',data: {message: 'Lemon'},methods: {},components: {cpn: {template: '#cpn',data() {return {name: '我是cpn的name'}},components: {ccpn}}}});</script>
</body>

组件化高级

slot-插槽的基本使用

<body><!-- 1.插槽的基本使用:<slot></slot>2.插槽的默认值:<slot>传的元素/值:eg 哈哈哈</slot>3.如果有多个值,同时被放入到组件中进行替换,会一起作为替换元素--><div id="app"><cpn>嘻嘻嘻</cpn><cpn><button>按钮</button></cpn><cpn>哈哈哈</cpn><cpn></cpn></div><template id="cpn"><div><h3>组件化开发</h3><slot><button>按钮</button></slot></div></template><script>const app = new Vue({el: '#app',data: {},methods: {},components: {cpn: {template: '#cpn'}}});</script>
</body>

slot-具名插槽的使用

<body><div id="app"><cpn>嘻嘻嘻</cpn><cpn><span slot="right">哈哈哈</span></cpn></div><template id="cpn"><div><slot name="left">左边</slot><slot name="center">中间</slot><slot name="right">右边</slot><slot>右边</slot></div></template><script>const app = new Vue({el: '#app',data: {},methods: {},components: {cpn: {template: '#cpn'}}});</script>
</body>

什么是编译的作用域

<body><!-- 总结:父组件模板的所有东西都会在父级作用域内编译子组件模板的所有东西都会在子级作用域内编译 --><div id="app"><!-- 这个里面的isShow会先从所在模板里面顺下去找,即从Vue里找寻,因此Vue中的data的isShow才能影响显示与否 --><cpn v-show="isShow"></cpn></div><template id="cpn"><div><h2>我是组件</h2><!-- 这个里面的isShowCpn会先从所在模板里面顺下去找,即从cpn里找寻, --><h3 v-show="isShowCpn">我是哈哈哈</h3></div></template><script>const app = new Vue({el: '#app',data: {isShow: true},methods: {},components: {cpn: {template: '#cpn',data() {return {isShow: false,isShowCpn: true}}}}});</script>
</body>

作用域插槽的准备

<body><!-- 作用域插槽:父组件替换插槽的标签,但是内容由子组件来提供 --><div id="app"><cpn></cpn><hr><cpn>哈哈哈哈·<!-- 目标获得子组件中的pLanguages --><template><div slot-scope="slot"><span v-for="(item,index) in slot.data">- {{item}} {{item}} - </span><hr></div></template></cpn><hr><cpn><!-- 目标获得子组件中的pLanguagesv-slot:todo todo指向slot中的name="todo" --><template v-slot:todo="slotProps"><div>加join():<span>- {{slotProps.data.join(' - ')}}</span><br><span>{{slotProps.data}}</span></div></template></cpn></div><template id="cpn"><div><!-- :名字="cpn中对应需要获取的值" --><slot :data="pLanguages"  name="todo"><ul><li v-for="(item,index) in pLanguages">{{item}}</li></ul></slot></div></template><script>const app = new Vue({el: '#app',data: {},methods: {},components: {cpn: {template: '#cpn',data() {return {pLanguages: ['Java', 'C', 'C++', 'Python', 'C#']}}}}});</script>
</body>

前端模块化

ES模块化的实现

aaa.js

var name = '小红'
let age = 18
var flag = truefunction sum(num1, num2) {return num1 + num2
}if (flag) {console.log(sum(200, 300));
}export {flag, sum
}

bbb.js

var name = '小红'
var flag = false// var name = '小明'
// let age = 18
// var flag = true// function sum(num1, num2) {//   return num1 + num2
// }// if (flag) {//   console.log(sum(20, 30));
// }// export {//   flag, sum
// }

index.html

<!DOCTYPE html>
<html lang="en">
<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"><title>Document</title>
</head>
<body><!-- 出现了跨域的问题,但是原理基本这样 --><script src="aaa.js" type="module"></script><script src="bbb.js" type="module"></script><script src="mmm.js" type="module"></script>
</body>
</html>

mmm.js

import {flag} from "./aaa.js";if (flag) {console.log('小明是天才,哈哈哈哈');
}

webpack使用

webpack的起步

info.js

export const name = 'why'
export const age = 18
export const height = 1.78

main.js

// 1.使用commonjs的模块化规范
const {add, mul} = require('./mathUtils.js')console.log(add(20, 30));
console.log(mul(25, 30));// 2.使用ES6的模块化的规范
import {name, age, height} from "./info";console.log(name);
console.log(age);
console.log(height);

mathUtils.js

function add(num1, num2) {return num1 + num2
}function mul(num1, num2) {return num1 * num2
}module.exports = {add,mul
}

index.html

<!DOCTYPE html>
<html lang="en">
<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"><title>Document</title>
</head>
<body><!-- PS D:\Web_Study\vue.js学习> cd  12-webpack使用\1-webpack的起步PS D:\Web_Study\vue.js学习\12-webpack使用\1-webpack的起步> webpack ./src/main.js ./dist/bundle.js --><!-- 生成文件的位置 --><script src="./dist/bundle.js"></script>
</body>
</html>

webpack的配置

webpack.config.js

const path = require('path')// npm init; npm installmodule.exports = {entry: './src/main.js',output: {// 动态获取路径:resolve拼接地址path: path.resolve(__dirname, 'dist'),filename: 'bundle.js'},
}

package.json

{"name": "meetwebpack","version": "1.0.0","description": "","main": "webpack.config.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","build": "webpack"},"author": "","license": "ISC","devDependencies": {"webpack": "^3.6.0"}
}

webpack配置loader和vue

webpack.config.js

const path = require('path')// npm init; npm installmodule.exports = {entry: './src/main.js',output: {// 动态获取路径:resolve拼接地址path: path.resolve(__dirname, 'dist'),filename: 'bundle.js',// 可以显示加载后的图片// publicPath: '/dist'publicPath: 'dist/'},module: {rules: [{test: /\.css$/i,use: ['style-loader', 'css-loader'],}, {test: /\.less$/,use: [{loader: "style-loader" // creates style nodes from JS strings}, {loader: "css-loader" // translates CSS into CommonJS}, {loader: "less-loader" // compiles Less to CSS}]}, {test: /\.(png|jpg|gif)$/,use: [{loader: 'url-loader',options: {// 如果limit小于文件大小 * 1024,就会报错,Cannot find module 'file-loader'// 一般配置成8kblimit: 8192,name: 'img/[name].[hash:8].[ext]'}}]}, {test: /\.js$/,// 排除exclude: /(node_modules|bower_components)/,use: {loader: 'babel-loader',options: {presets: ['es2015']}}}],},
}

html

index.html

<!DOCTYPE html>
<html lang="en"><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"><title>Document</title>
</head><body><!-- webpack官网:https://www.webpackjs.com/loaders/babel-loader/重命名会让配置正确的出错,需要重新安装如若出现,ERROR in Entry module not found: Error: Can't resolve 'babel-loader' in 'D:\Web_Study\vue.js学习\12-webpack使用\4-webpack配置vue'请 cnpm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env webpack0. 动态获取webpack的dist配置路径//入口entry: './src/main.js',//输出output: {// 动态获取路径:resolve拼接地址path: path.resolve(__dirname, 'dist'),filename: 'bundle.js',},使用 cnpm run build 启动方式配值:1.安装本地webpack2.在package.json文件中的script添加 "build": "webpack"3.然后输入cnpm run build,就会在webpack.config.js中根据动态路径创建4.加载css文件:cnpm install --save-dev css-loader5.解析加载进去的css文件:cnpm install --save-dev style-loader6.在webpack.config.js中的module.exports = {}中添加module: {rules: [{test: /\.css$/i,use: ['style-loader', 'css-loader'],},],},7.导入less文件,并转换成css文件:cnpm install --save-dev less-loader less8.在webpack.config.js中的module.exports = {}中添加module: {rules: [{test: /\.less$/,use: [{loader: "style-loader" // creates style nodes from JS strings}, {loader: "css-loader" // translates CSS into CommonJS}, {loader: "less-loader" // compiles Less to CSS}]}]}9.加载图片:cnpm install --save-dev url-loader10.{test: /\.(png|jpg|gif)$/,use: [{loader: 'url-loader',options: {// 如果limit小于文件大小 * 1024,就会报错,Cannot find module 'file-loader'// 如果要加载的图片小于8kb,就会将加载的图片转换成base64// 一般配置成8kblimit:8192}}]}11.404  会把图片发布在dist文件夹里,但是我们css文件中还是在找img里的图片GET file:///D:/Web_Study/vue.js%E5%AD%A6%E4%B9%A0/12-webpack%E4%BD%BF%E7%94%A8/4-webpack%E7%9A%84lcss%E6%A0%B7%E5%BC%8F-less%E6%96%87%E4%BB%B6/c67dcb9e8b50af7c2550f6da0c40f7e0.jpg net::ERR_FILE_NOT_FOUND12. 解决方法:可以显示加载后的图片 publicPath: '/dist'在webpack.config.js中的output里添加// 可以显示加载后的图片publicPath: '/dist'13. 让加载后的图片在dist里的指定目录:图片名字  img文件夹/[name]此为变量名/hash为32位,截取8位/ext:拓展名注意:若只写name就是固定值,即一直都是这个名字name: 'img/[name].[hash:8].[ext]'14. 显示图片:publicPath: 'dist/'15. ES6语法 转换成 ES5语法:①:cnpm install --save-dev babel-loader@7 babel-core babel-preset-es2015②:配置文件中添加:module: {rules: [{test: /\.js$/,exclude: /(node_modules|bower_components)/,use: {loader: 'babel-loader',options: {presets: ['@babel/preset-env']}}}]}--><script src="./dist/bundle.js"></script>
</body></html>

js

main.js

// 1.使用commonjs的模块化规范
const {add, mul} = require('./js/mathUtils.js')console.log(add(20, 30));
console.log(mul(25, 30));// 2.使用ES6的模块化的规范
import {name, age, height} from "./js/info";console.log(name);
console.log(age);
console.log(height);// 3.依赖css文件
require('./css/normal.css')// 4.依赖less文件
require('./css/special.less')
document.writeln('<h2>你好呀,李银河</h2>')

info.js

export const name = 'why'
export const age = 18
export const height = 1.78

mathUtils.js

function add(num1, num2) {return num1 + num2
}function mul(num1, num2) {return num1 * num2
}module.exports = {add,mul
}

css

normal.css

body {/* background: mediumaquamarine; */background: url("../img/timg1.jpg")
}

special.less

@fontSize: 50px;
@fontColor: orange;body {font-size: @fontSize;color: @fontColor
}

模块展示

vue-cli3使用

App.vue

<template><div id="app"><img alt="Vue logo" src="./assets/logo.png"><HelloWorld msg="Welcome to Your Vue.js App"/></div>
</template><script>
import HelloWorld from './components/HelloWorld.vue'export default {name: 'app',components: {HelloWorld}
}
</script><style>
#app {font-family: 'Avenir', Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;
}
</style>

HelloWorld.vue

<template><div class="hello"><h1>{{ msg }}</h1></div>
</template><script>
export default {name: 'HelloWorld',props: {msg: String}
}
</script><style scoped>
h3 {margin: 40px 0 0;
}
ul {list-style-type: none;padding: 0;
}
li {display: inline-block;margin: 0 10px;
}
a {color: #42b983;
}
</style>

main.js

import Vue from 'vue'
import App from './App.vue'Vue.config.productionTip = falsenew Vue({// 使用的是runtime-onlyrender: h => h(App),// render: h => {//   return h(App)// }
}).$mount('#app')

带你从Vue入门到进阶相关推荐

  1. python带我起飞_Python带我起飞:入门、进阶、商业实战

    <Python带我起飞:入门.进阶.商业实战>针对Python3.5以上版本,采用"理论+实践"的形式编写,通过大量的实例(共42个),全面而深入地讲解"Py ...

  2. 后端小白的VUE入门笔记, 进阶篇

    使用 vue-cli( 脚手架) 搭建项目 基于vue-cli 创建一个模板项目 通过 npm root -g 可以查看vue全局安装目录,进而知道自己有没有安装vue-cli如果没有安装的话,使用如 ...

  3. 【Python基础】Matplotlib 实操干货,38个案例带你从入门到进阶!

    译文出品:Python数据之道 原文作者:Rizky Maulana Nurhidayat 翻译:Lemon Matplotlib 实操干货, 38个案例带你从入门到进阶! 「Python数据之道」注 ...

  4. 100例Python代码带你从入门到进阶!

    以下所有代码全都至少运行一遍,确保可复现.易于理解.逐步完成入门到进阶的学习. 此教程经过我 反复打磨多遍 ,经常为此熬夜,真心不易,文章比较长,看完有用,帮我点个在看或分享支持. 教程包括 62 个 ...

  5. C语言推荐书籍pdf版附下载链接共30+本从入门到进阶带你走上大牛之路

    前言: 技术书阅读方法论 一.速读一遍(最好在1~2天内完成) 人的大脑记忆力有限,在一天内快速看完一本书会在大脑里留下深刻印象,对于之后复习以及总结都会有特别好的作用. 对于每一章的知识,先阅读标题 ...

  6. 前端开发从入门到进阶完全指南,不用再迷茫前端要怎么学啦!

    我经常会看到很多同学在学习前端的时候比较迷茫,不知道到底应该以怎样的学习路线来入门和进阶前端领域.每次遇到这种问题我也会分享一下自己的学习经验,但是发现这是一个问得非常多的一个共性问题. 作为程序员, ...

  7. 还在迷茫于前端如何入门和进阶?万字指南让你不再迷茫!

    我经常会看到很多同学在学习前端的时候比较迷茫,不知道到底应该以怎样的学习路线来入门和进阶前端领域.每次遇到这种问题我也会分享一下自己的学习经验,但是发现这是一个问得非常多的一个共性问题. 作为程序员, ...

  8. Vue3+TypeScript从入门到进阶(六)——TypeScript知识点——附沿途学习案例及项目实战代码

    文章目录 一.简介 二.Vue2和Vue3区别 三.Vue知识点学习 四.TypeScript知识点 一.JavaScript和TypeScript 二.TypeScript的安装和使用 1.Type ...

  9. Webpack实战:入门、进阶与调优(第2版)

    你是否经常听到项目组的同事抱怨:"为什么Webpack这么慢?""为什么Webpack又出错了?" "发布到线上的代码为什么不能正常工作?" ...

最新文章

  1. python爬虫执行js代码_爬虫之python3用execjs执行JS代码
  2. SpringMVC 原理和流程
  3. 你需要了解的 C++ 17 Top 19 新特性(附精彩评论)
  4. SpringMVC框架结构以及架构流程
  5. python算法与数据结构-插入排序算法
  6. Maven 项目创建 找不到web.xml
  7. Github Pages 搭建网站
  8. Koa 还是 Express
  9. Android之CheckBox进行代码设置setChecked(true)会触发setOnCheckedChangeListener事件
  10. java控制台打印图片_java——控制台输入打印图形
  11. mnist手写数字数据集_mnist手写数据集(1. 加载与可视化)
  12. Bash中的位置参数和特殊参数
  13. “天池”淘宝用户消费行为分析——sql
  14. 半导体存储器件原理概述
  15. 学生宿舍管理系统java课设_JAVA学生宿舍管理系统
  16. gdb: warning Can‘t open file /usr/lib/libstdc++.so.6.0.19 during file-backed mapping note processing
  17. 中职学校计算机基础的重要性,自学能力论文,关于浅淡培养中职学生计算机自学能力的重要性和方法相关参考文献资料-免费论文范文...
  18. 普里姆(Prim)算法 Java实现(最小生成树)
  19. 基于OpenCvSharp的数字图像处理 - 图像彩色类型转换
  20. linux系统安装fio工具步骤

热门文章

  1. ios 自定义拍照页面_iOS开发笔记:自定义相机拍照
  2. 数据分析:大数据时代的必备技能之EXCEL
  3. PT100转RS485热电阻Modbus低成本数据采集模块
  4. Python pandas库|任凭弱水三千,我只取一瓢饮(5)
  5. 用应用软件UCCW Widget制作出属于你独一无二的梦幻桌面吧!
  6. 三星老java手机换字体_三星手机设置字体大小与更换默认字体的图文教程
  7. 20175208 实验三《敏捷开发与XP实践》_实验报告
  8. jQuery ajax请求两次问题,jquery ajax请求了两次问题
  9. flex布局小案例——制作骰子
  10. 华为吹响“伙伴暨开发者”集结号:共赢数字时代