学习路线图: HBuilderX工具的使得->了解Vue工作原理-> 模板语法->组件化->vue-cli完整项目开发
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
学习Vue需要有一定的预备知识, 需要了解HTML、CSS 和 JavaScript 的中级知识。如果你刚开始学习前端开发,将框架作为你的第一步可能不是最好的主意——掌握好基础知识再来吧!之前有其它框架的使用经验会有帮助,但这不是必需的。
MVC 模式(Model–View–Controller)是软件工程中的一种软件架构模式,它把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)
MVVM是由MVC进一步发展而来, 还是分为三个部分: 模型(Model)、视图(View)和控制器(VM)
数据驱动(即是双向的数据绑定) DOM是数据的一种自然映射。双向是指:HTML标签数据 绑定到 Vue对象,另外反方向数据也是绑定的。Vue对象的改变会直接影响到HTML的标签的变化,而且标签的变化也会反过来影响Vue对象的属性的变化。之前DOM驱动的开发方式尤其是以jQuery为主的开发时代,都是DOM变化后,触发js事件,然后在事件中通过js代码取得标签的变化,再跟后台进行交互,然后根据后台返回的结果再更新HTML标签,异常的繁琐。有了Vue这种双向绑定, 让开发人员只需要关心json数据的变化即可,Vue自动映射到HTML上,而且HTML的变化也会映射回js对象上,开发方式直接变革成了前端由数据驱动的开发时代。
https://baike.baidu.com/pic/MVVM/96310/0/e61190ef76c6a7efe4baffc3fdfaaf51f2de66b2?fr=lemma&ct=single
###3. 模板指令
开发工具的选择: 开发vue的工具有很多,比较主流的有vscode、hbuild、webstorm,官方推荐使用hbuildx。可以到官方网站上下载hbuildX软件。
Vue 可以直接把它当做一个js库使用,可以很简单的接入到你的项目中,或者你只需要使用双向数据绑定。
需求: 网页中有个div标签, 而需要有json对象存储数据,把json对象上的数据放到div。
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<div id='app'>app>
创建Vue的对象并把数据绑定到上 创建好的div上去, 以下是完整的代码
<head>
<meta charset="utf-8">
<title>title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>
<body>
<div id="app">
<p>message: {{ message }}p>
div>
body>
<script>
new Vue({
// el: element, 让vue绑定一个元素节点(使用css选择器进行匹配)
el: '#app',
// data: 数据选项,用来存放数据
data: {
message: "教育"
},
})
script>
通过模版指令(写在html中的),即是html和vue对象的粘合剂。
数据渲染有v-text
、v-html
、{{ }}
mustache三种方式,用法如下:
<body>
<div id="app">
<div>
<p>{{ name + 1 }}p>
<p v-text="name">p>
<p v-html="name">p>
div>
div>
body>
<script>
new Vue({
el: '#app',
data: {
name: "教育"
}
})
script>
v-text更新元素的textContent, v-html更新元素的innerHTML ,内容按普通HTML插入,不会作为 Vue 模板进行编译。
主要用于显示隐藏,包括v-if
、v-show
、v-else
、v-else-if
,用法如下:
<body>
<div id="app">
<div>
<p v-if="isShow">科比p>
<p v-else="isShow">詹姆斯p>
<p v-show="isShow">库里p>
div>
div>
body>
<script>
new Vue({
el: '#app',
data: {
isShow: false
}
})
script>
v-if是直接不渲染该元素; v-show是通过display: none进行隐藏;
模板引擎都会提供循环的支持,渲染循环使用v-for
。 基本的用法类似于foreach的用法,详细参考以下代码:
<body>
<div id="app">
<div>
<ul>
<li v-for="item in stars">
{{ item.name }}
li>
ul>
<ul>
<li v-for="(item, index) in stars">
{{index}}: {{ item.name }}
li>
ul>
div>
div>
body>
<script>
new Vue({
el: '#app',
data: {
stars: [
{name: "鹿晗"},
{name: "蔡徐坤"},
{name: "李易峰"},
{name: "吴亦凡"},
],
}
})
script>
事件绑定使用v-on:事件名
或者快捷方式@事件名
,详细用法如下:
<body>
<div id="app">
<p v-show="isShow">科比p>
<div>
<button v-on:click="btnClick">按钮button>
<button @click="btnClick">按钮button>
div>
div>
body>
<script>
new Vue({
el: '#app',
data: {
isShow: false
},
methods:{
btnClick() {
this.isShow = !this.isShow
}
}
})
script>
示例2:
-
{{ index }}- {{ item.name }}
{{ item.id }}
Vue中不能直接使 {{ expression}} 语法进行绑定html的标签,而是用它特有的v-bind
指令。
语法: <标签 v-bind:属性名="要绑定的Vue对象的data里的属性名">标签>
。
由于v-bind
使用非常频繁,所以Vue提供了简单的写法,可以去掉v-bind
直接使用:
即可。
<body>
<div id="app">
<div>
<img v-bind:src="imgsrc" width="100" />
<img :src="imgsrc" width="100" />
div>
div>
body>
<script>
new Vue({
el: '#app',
data: {
imgsrc: 'https://cn.vuejs.org/images/logo.png'
}
})
script>
示例2:
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue二之数据绑定title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<style>
.small{
width: 100px;
height: 60px;
}
.big{
width: 400px;
height: 300px;
}
style>
head>
<body>
<div id="app">
<button @click="cover_cls='big'">放大图片button>
<button @click="cover_cls='small'">缩小图片button>
<button @click="width -= 10">减宽button>
<button @click="width += 10">加宽button>
<button @click="height -= 10">差高button>
<button @click="height += 10">加高button>
<br>
<img
:width="width" :height="height"
:src="cover" @click="swithCover">
div>
<script>
let covers = [
'http://photo.tuchong.com/3199480/f/22860762.jpg',
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fphoto.tuchong.com%2F19348387%2Ff%2F811803390.jpg&refer=http%3A%2F%2Fphoto.tuchong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639019842&t=c08a6daebe3f6ba7ed23dad4eb87fe47',
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fww2.sinaimg.cn%2Fmw690%2F9e3738f9gy1gw6vmdezsxj20u00mi45r.jpg&refer=http%3A%2F%2Fwww.sina.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639019842&t=e28ab3482c54f1d7a58e721dfb83b94f',
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fww1.sinaimg.cn%2Fmw690%2F9e3738f9gy1gw6vmds4q4j20u00mjwnb.jpg&refer=http%3A%2F%2Fwww.sina.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639019842&t=ac697b1c5089740f9cca5531458d5233'
]
new Vue({
el: '#app',
data: {
cover: 'http://photo.tuchong.com/3199480/f/22860762.jpg',
cover_cls: 'big',
width: 200,
height: 300
},
methods: {
swithCover(){
index = Math.floor(Math.random()*4)
console.info('--->cover index: '+index)
// this是当前Vue的实例对象
this.cover = covers[index]
}
}
})
script>
body>
html>
针对的是输入框input和form表单
双向数据绑定使用v-model
,实现两个组件的数据同步,如本文框与计算结果同步。
<body>
<div id="app">
<div>
<input type="text" v-model="val">
val值: {{val}}
div>
div>
body>
<script>
new Vue({
el: '#app',
data: {
val:"千锋教育"
}
})
script>
Vue的实例是Vue框架的入口,其实也就是前端的ViewModel,它包含了页面中的业务逻辑处理、数据模型等,当然它也有自己的一系列的生命周期的事件钩子 ,辅助我们进行对整个Vue实例生成、编译、 挂载、销毁等过程进 js控制。
data数据选项
代表vue对象的数据 创建的Vue对象中的data属性就是用来绑定数据到HTML的, Vue框架会自动监视data里面的数据变化,自动更新数据到HTML标签上去。
methods方法选项
methods方法选项代表vue对象的方法, 方法中的 this 自动绑定为 Vue 实例。
computed计算属性
在做数据的绑定的时候,数据要进行处理之后才能展示到html页面上,虽然vue提供了非常好的表达式绑定的方法,但是只能应对低强度的需求,另外放入太多的逻辑会让模板过重且难以维护。 而计算属性,即属性可以是一个方法
设置了对象的监听方法, 一个对象,键是需要观察的表达式,值是对应回调函数。 值也可以是方法名,或者包含选项的对象。
我们通过一个案例来了解以上介绍的几个重要选项的运用
<head>
<meta charset="utf-8">
<title>title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>
<body>
<div id="app">
iphone11Pro Max
<p>价格: {{ price }}p>
<p>数量:
<button @click="sub">-button>
{{ num }}
<button @click="add">+button>
p>
<p>总价: {{ total }}p>
div>
body>
<script>
new Vue({
el: "#app",
// 数据选项
data: {
price: 10000,
num: 1,
},
// 方法选项
methods: {
sub() {
this.num--
},
add() {
this.num++
}
},
// 计算属性选项
computed: {
total() {
return this.price * this.num;
}
},
// watch: 监听属性选项
watch: {
num(newVal, oldVal) {
if (newVal < 1 || newVal > 10) {
this.num = oldVal;
}
}
}
})
script>
组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。
Vue组件 *.vue 由三部分组成分别是:
HTML代码
javascript脚本
css样式。【官方推荐】将三个部分都写到一个 .vue 件中。
全局组件可以应用于多个Vue实例中,使用 Vue.component(tagName, options)
,如下:
Vue.component('my-component', {
template: 'A custom component!'
})
【注意】自定义标签名,但尽量遵循W3C规则(小写,并且包含一个短杠),vue中不强制遵循这一规则。
局部组件一般仅在某一个Vue实例中使用,从作用域来说是局部的,如:
var Child = {
template: 'A custom component!'
}
new Vue({
// ...
components: {
// 将只在父模板可用
'my-component': Child
}
})
使用组件非常简单,在vue实例渲染的页面标签中,将组件名作为标签名使用即可:
<div id="main">
<my-component>simple-counter>
<my-component>simple-counter>
div>
综合示例:
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组件入门title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>
<body>
<div id="app">
<div>
<el-button msg='Ok'>el-button>
<el-button msg='Cancel'>el-button>
<el-label title='价格'>el-label>
div>
div>
<div id="app2">
<el-button msg='yes'>el-button>
<el-button msg='no'>el-button>
<el-label title='名称'>el-label>
div>
<script>
// 全局的组件
Vue.component('el-button', {
template: '',
props: {
msg: String
},
data(){
return {}
}
})
let vm1= new Vue({
el: '#app',
components:{
'el-label': {
template: '',
props: {
title: String
}
}
}
})
let vm2 = new Vue({
el: '#app2'
})
script>
body>
html>
组件模板中使用的变量数据,可以在组件data(这是一个函数)中声明,如:
var data = { counter: 0 } // 全局变量,组件被多次使用时,共用一个data
Vue.component('simple-counter', {
template: '',
data: function () {
return data
}
})
new Vue({
el: '#example-2'
})
以上代码中,data声明是全局变量,通过以下页面渲染后,点击某一个按钮会影响其它按钮的结果。
<div id="example-2">
<simple-counter>simple-counter>
<simple-counter>simple-counter>
<simple-counter>simple-counter>
div>
因为data是全局的变量,会影响其它相同的组件显示的值,因此可以局部变量,如下:
Vue.component('simple-counter', {
template: '',
data: function () {
return { counter: 0 } // 每渲染一次组件,则返回新的data对象
}
})
new Vue({
el: '#example-2'
})
组件意味着协同工作,通常父子组件会是这样的关系:组件 A 在它的模版中使用了组件 B 。它们之间必然需要相互通信:父组件要给子组件传递数据,子组件需要将它内部发生的事情告知给父组件。然而,在一个良好定义的接口中尽可能将父子组件解耦是很重要的。这保证了每个组件可以在相对隔离的环境中书写和理解,也大幅提高了组件的可维护性和可重用性。
在 Vue.js 中,父子组件的关系可以总结为 props down, events up 。父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。如下图所示:
父层向子层传值使用props
属性,如下:
Vue.component('child', {
props: ['message'],
template: '{{ message }}'
})
在父层模板中,使用如下:
<child message="hello!">child>
当然如果使用父层vue实例的data中变量(动态绑定)也可以, 如下所示:
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg">child>
div>
【注意】在props
声明属性时,可以指定属性类型(Number、String、Boolean、Array、Object、Function)和required属性(boolean类型,表示是否必要属性),如下所示:
Vue.component('example', {
props: {
// 基础类型检测 (`null` 意思是任何类型都可以)
propA: Number,
// 多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数字,有默认值
propD: {
type: Number,
default: 100
},
// 数组/对象的默认值应当由一个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}
})
子层(组件)将内部发生变量的数据向父层传递时,一般使用v-on
事件。
当然每个Vue实例都实现了事件接口(Events interface):
案例如下:
<div id="counter-event-example">
<p>{{ total }}p>
<button-counter v-on:increment="incrementTotal">button-counter>
<button-counter v-on:increment="incrementTotal">button-counter>
div>
父层模块中监听increment
事件,监听的方法是incrementTotal
。
// 子层
Vue.component('button-counter', {
template: '',
data: function () {
return {
counter: 0
}
},
methods: {
increment: function () {
this.counter += 1
this.$emit('increment')
}
},
})
// 父层
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
在子层组件中绑定数据和事件,并在事件方法中通过组件对象的$emit
触发外部组件(父级)事件。
综合练习:
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>组件化的记数器title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>
<body>
<div id="app">
<p>总数: {{ total }}p>
<el-counter @add='addHandler' @sub='subHandler'>el-counter>
<el-counter @add='addHandler' @sub='subHandler'>el-counter>
<el-counter @add='addHandler' @sub='subHandler'>el-counter>
<el-counter @add='addHandler' @sub='subHandler'>el-counter>
<el-counter @add='addHandler' @sub='subHandler'>el-counter>
div>
<script>
let data = {n: 0} // 全局的数据
Vue.component('el-counter', {
template: '{{ n }}',
data(){
return {n: 0} // 局部的数据
},
watch:{
n(n_, o_){
if(n_ < 0){
this.n = o_
}
}
},
methods:{
add(){
this.n++
this.$emit('add')
},
sub(){
this.n--
if(this.n >= 0)
this.$emit('sub')
}
}
})
new Vue({
el: '#app',
data: {
total: 0
},
methods:{
addHandler(){
//接收子组件的加1的信号
this.total++
},
subHandler(){
//接收子组件的减1的信号
this.total--
}
}
})
script>
body>
html>
购物车实战:
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>购物车效果title>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<style>
.row{
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
flex-wrap: nowrap;
}
.row .cover{
height: 100px;
width: 80px;
border-radius: 10px;
box-shadow: 5px 5px 5px lightcyan;
margin: 5px;
}
.bold {
font-weight: bold;
color: blue;
}
.content {
width: 100%;
}
.content>div{
margin: 5px;
}
.btn{
width: 100%;
height: 45px;
background-color: red;
color: white;
padding: 10px;
}
.btn:active{
opacity: 0.8;
}
.btn:hover{
cursor: pointer;
background-color: darkred;
}
ul,li{
margin: 0;
padding: 0;
list-style: none;
}
.col{
display: flex;
flex-direction: column;
flex-wrap: nowrap;
}
.total{
display: inline-block;
background-color: red;
color: white;
width: 15px;
height: 15px;
border-radius: 50%;
font-size: 0.5rem;
text-align: center;
opacity: 0.8;
}
.top-total{
text-align: right;
position: relative;
right: 10px;
top: 10px;
}
.cart {
background-color: lightgray;
padding-bottom: 5px;
border: 1px solid lightblue;
height: 40px;
}
.cart-div{
width: 800px;
height: 600px;
position: absolute;
background-color: lightcyan;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
style>
head>
<body>
<div id="app">
<div style="width: 100px; height: 35px;" class="cart" @click="isshow=true">
<div class="top-total"><span class="total">{{cart_total}}span>div>
<div >我的购物车div>
div>
<ul>
<li v-for="g in goods">
<div class="row">
<img class="cover" :src="'http://182.61.29.85'+ g.goodsimg">
<div class="content">
<div class="bold">{{ g.goodsname }}div>
<div>
原价: <span style="text-decoration: line-through;color:lightgray"> ¥{{ Math.round(parseFloat(g.price) * 1.5) + '.00'}}span>
<span style="color: red;">会员价: ¥{{ g.price }}span>
div>
<div>
<span>限购件数: {{ g.sales_volume }}span>
div>
div>
<div style="text-align: right; width: 200px">
<span @click="addCart(g)" class="btn">加入购物车span>
div>
div>
li>
ul>
<div class="cart-div" v-show="isshow">
<h3>我的购物车h3>
<button @click="isshow=false">关闭button>
<ul>
<li v-for="g in carts">
{{ g.name }}-{{g.price}} <button>-button> <input v-model="g.cnt" size="1"><button>+button>
li>
ul>
<div>
总价格: ¥ {{ total_price }}
div>
div>
div>
<script>
// Vue生命周期的方法: create,mounte,update, destroy
axios.defaults.baseURL = 'http://182.61.29.85'
new Vue({
el: '#app',
data: {
goods: [],
cart_total: 0,
carts: [],
isshow: false
},
created() {
axios.get('/api/new/').then(response=>{
console.info(response)
if(response.status === 200){
// alert('已加载数据')
// response.data => {msg:'', data: []}
this.goods = response.data
console.info(this.goods)
}
}).catch(error=>{
console.error(error)
})
},
methods: {
addCart(item){
this.carts.push({name: item.goodsname, price: item.price, cnt: 1})
this.cart_total ++
}
},
computed:{
total_price(){
let tp = 0
this.carts.forEach(item=>{
tp += item.price*item.cnt
})
return tp
}
}
})
script>
body>
html>
官网下载地址: https://code.visualstudio.com/
vscode安装的插件较多,详细如下:
下载一个chrome的助手(链接:https://pan.baidu.com/s/1u8vqWJUSedASDPFC9op70Q 密码:7w4c)
下载完成后,自行解压,然后打开chrome的插件管理器(chrome://extensions/), 点击"加载已解压的扩展程序",在弹出的文件目录选择对话框中,选择chrome助手的目录即可。
在github上下载压缩包并解压到本地,github下载地址:https://github.com/vuejs/vue-devtools
下载完成后打开命令行cmd进入vue-devtools-master文件夹,执行如下命令:
npm install
npm run build
编译完成之后,将生成的shells下的chrome文件夹安装到chrome扩展程序中即可。
标签名>二级标签名*n>三级标签名*n + tab键
选择某一标签区域 + 快捷键 ctrl+]
ctrl+Shift 取消(向左缩进)
tab 向右缩进
Ctrl+Shift+] 可去掉父级标签并自动处理子节点的缩进
Ctrl+Shift+k,可以把每个css的区块代码都合并为一行
ctrl+k, 自动格式化换行
ctrl + / 行注释
ctrl+alt + / 块注释