Vue.js 官网:cn.vuejs.org/
一、Vue/jquery/es6 的区别
1. ES6
<h2 class="title">{{words}}</h2>
<script>
// ES6
document.querySelector(".title").textContent = "Hello World";
</script>
2. jQuery
<h2 class="title">{{words}}</h2>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script>
// jQuery
$(".title").text("Hello World");
</script>
3. Vue
<h2 class="title">{{words}}</h2>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// Vue.js
const vm = new Vue({
el: document.querySelector(".title"),
data: {
words: "HELLO WORLD",
},
});
</script>
二、挂载点/数据注入/响应式
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 创建一个Vue的根节点,Vue实例的作用域 -->
<div class="app">
<p>用户名: {{username}}</p>
<p>{{username + ', php中文网讲师'}}</p>
<p>37 + 43 = {{37 + 43}}</p>
<p>{{flag ? '高兴' : '睡觉'}}</p>
</div>
<script>
// 创建Vue实例
const vm = new Vue({
// 当前vue实例的配置
// 1. 挂载点
// el: document.querySelector('.app'),
el: ".app",
// 2. 数据注入/绑定,注入到了当前vue实例中
data: {
username: "天蓬老师",
flag: false,
},
});
console.log(vm.$el);
// 既然$data已注入到vue实例中, 那么就可能当成vue的属性输出
console.log(vm.$data.username);
console.log(vm.username);
// 3. 响应式
vm.username = "灭绝师妹";
</script>
三、v-text/v-once/v-html
<div class="app">
<!-- <p>用户名: {{username}}</p> -->
<!-- vue中的指令以v-为前缀 -->
<!-- v-text : textContent -->
<p>用户名: <span v-text="username"></span></p>
<!-- v-html : innerHTML -->
<p>用户名: <span v-html="username"></span></p>
<!-- <p>用户名: <span v-once="username"></span></p> -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const vm = new Vue({
el: ".app",
data: {
username: "小张老师",
},
});
vm.username = '<em style="color:red">西门老师</em>';
</script>
四、属性指令:v-bind/v-on
<!-- <div class="app" style="color: red" onclick="alert(this.textContent)">hello</div> -->
<div class="app">
<p v-bind:style="style1">{{site}}</p>
<!-- v-bind: 可简单化为":", 为元素绑定普通属性 -->
<p v-bind:style="style1, style2">{{site}}</p>
<p v-bind:style="['background: yellow', 'color: red']">{{site}}</p>
<!-- v-on: 添加事件指令 -->
<!-- <p><a href="https://php.cn" v-on:click="show">网站名称-1</a></p> -->
<!-- v-on: @ -->
<!-- prevent: 修饰符, 防止默认行 -->
<p onclick="console.log(this.tagName)"><a href="https://php.cn" @click.prevent="show">网站名称-1</a></p>
<!-- stop: 防止冒泡 -->
<p onclick="console.log(this.tagName)"><a href="https://php.cn" @click.prevent.stop="show">网站名称-1</a></p>
<!-- 事件传参 -->
<button @click.stop="calc($event, 40, 200)">计算40 + 200 = ?</button>
{{result}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const vm = new Vue({
el: ".app",
data: {
site: "php中文网",
style1: "color: red",
style2: "background: yellow",
result: 0,
},
// 事件方法
methods: {
show() {
// this --> vue实例
console.log(this.site);
// debugger;
// ev.preventDefault();
},
calc(ev, x, y) {
this.result = `${x} + ${y} = ${x + y}`;
},
},
});
</script>
五、双向绑定:v-model
<div class="app">
<p>模型中的数据: {{site}}</p>
<p>双向绑定<input type="text" :value="site" @input="site=$event.target.value" /></p>
<!-- vue提供一个语法糖来快速实现这个功能: v-model -->
<hr />
<!-- 实时更新绑定 -->
<p>双向绑定/实时更新绑定<input type="text" :value="site" v-model="site" /></p>
<hr />
<!-- 延迟更新绑定, 修饰符: lazy -->
<p>双向绑定/延迟更新绑定<input type="text" :value="site" v-model.lazy="site" /></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const vm = new Vue({
el: ".app",
data: {
site: "php中文网",
},
});
// vm.site = "php.cn";
</script>
六、列表渲染:v-for
<!-- 挂载点 -->
<div class="app">
<!-- 遍历数组 -->
<ul>
<!-- <li>{{items[0]}}</li>
<li>{{items[1]}}</li>
<li>{{items[2]}}</li> -->
<!-- v-for : for of -->
<!-- :key 自定义键属性,主要是功能是干扰diff算法 -->
<li v-for="(item,index) of items" :key="index">[ {{index}} ] => {{item}}</li>
</ul>
<!-- 遍历对象 -->
<ul>
<!-- prop: 字符属性, index: 数值属性 -->
<li v-for="(item,prop, index) of user" :key="index">{{prop}}: {{index}} => {{item}}</li>
</ul>
<!-- 遍历对象数组 -->
<ul>
<li v-for="(user, index) of users" :key="user.id">{{user.id}}: {{user.name}} : [{{user.email}}]</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: ".app",
data: {
// 数组
items: ["合肥", "苏州", "杭州"],
// 对象
user: {
name: "朱老师",
email: "laozhu@qq.com",
},
// 对象数组
users: [
{ id: 1, name: "朱老师", email: "laozhu@qq.com" },
{ id: 2, name: "西门老师", email: "ximeng@qq.com" },
{ id: 3, name: "灭绝老师", email: "miejue@qq.com" },
],
},
});
// v-for : (循环变量, 索引/键) in/of 数组/对象 :key
// for - of
// for - in
// for , while
</script>
七、条件渲染:v-if/v-show
<div class="app">
<!-- 单分支 -->
<p v-if="flag">{{msg}}</p>
<button @click="flag = !flag" v-text="tips = flag ? `隐藏`: `显示`"></button>
<!-- 多分支 -->
<p v-if="point > 1000 && point < 2000">{{grade[0]}}</p>
<p v-else-if="point >= 2000 && point < 3000">{{grade[1]}}</p>
<p v-else-if="point >= 3000 && point < 4000">{{grade[2]}}</p>
<p v-else-if="point >= 4000">{{grade[3]}}</p>
<p v-else>{{grade[4]}}</p>
<!-- v-show: 元素依然在源码中,只是display:none -->
<p v-show="state">前端马上就要结束了</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: ".app",
data() {
return {
msg: "明晚我请大家吃饭,好吗?",
flag: false,
tips: "隐藏",
grade: ["纸片会员", "木头会员", "铁皮会员", "金牌会员", "非会员"],
point: 500,
state: false,
};
},
});
</script>
八、键盘修饰符:todolist
<div class="app">
<!-- <input type="text" @keyup="submit($event)" /> -->
<!-- enter: 回车的修饰符 -->
<input type="text" @keyup.enter="submit($event)" />
<ul>
<li v-for="(item, index) of list" :key="index">{{item}}</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: ".app",
data() {
return {
list: [],
};
},
methods: {
submit(ev) {
// console.log(ev.key);
// if (ev.key === "Enter") {
// this.list.unshift(ev.target.value);
// ev.target.value = null;
// }
this.list.unshift(ev.target.value);
ev.target.value = null;
},
},
});
</script>
九、计算属性
<div class="app">
<p>数量: <input type="number" v-model="num" style="width: 5em" min="0" /></p>
<p>单价: {{price}} 元</p>
<!-- <p>金额: {{num * price}}</p> -->
<!-- <p>金额: {{res}}</p> -->
<p>金额: {{amount}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const vm = new Vue({
el: ".app",
data() {
return {
num: 0,
price: 50,
res: 0,
};
},
// 计算属性
computed: {
amount: {
get() {
return this.num * this.price;
},
set(value) {
console.log(value);
if (value >= 2000) this.price = 40;
},
},
},
methods: {},
});
vm.amount = 2000;
</script>
十、侦听器属性
<div class="app">
<p>数量: <input type="number" v-model="num" style="width: 5em" min="0" :max="max"/></p>
<p>单价: {{price}} 元</p>
<p>金额: {{res}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const vm = new Vue({
el: ".app",
data() {
return {
num: 0,
price: 50,
res: 0,
max: 100,
};
},
// 侦听器属性
watch: {
// 是用来监听某一个属性的值的变化,属性名要和data字段中的变量名相同
// 例如,我要监听"num"变量的变化
// num(新的值,原来的值)
num(newVal, oldVal) {
console.log(newVal,oldVal);
// this.res = this.num * this.price;
// this.num --> newVal, this.price
this.res = newVal * this.price;
// 监听库存
if (newVal >= 20) {
this.max = newVal;
alert('库存不足, 请进货')
}
}
}
});
</script>
十一、组件的创建与注册
<!-- 挂载点: 根组件 -->
<div id="root">
<child-component></child-component>
</div>
<script>
// 组件本质上就是一组自定义的标签,可以在html文档出现
// 这个自定义的标签,需要用户自定义的功能,作用,行为
// 创建
// const h2 = document.createElement("h2");
// h2.textContent = "hello vue.js";
// 注册
// document.body.append(h2);
// 组件是可复用的vue实例,
// 1. 创建组件
// const childComponent = Vue.extend({
// template: `<h2> "hello vue.js"</h2> `,
// });
// 2. 注册组件
// Vue.component("child-component", childComponent);
// 二合一
Vue.component("child-component", {
template: `<h2>hello vue.js</h2>`,
});
// 3. 挂载组件
new Vue({
el: "#root",
});
// 注册(直接将创建合并到注册中) + 挂载
</script>
十二、全局组件
<div id="root">
<button-inc></button-inc>
</div>
<div id="root1">
<button-inc></button-inc>
</div>
<script>
// 全局组件: 声明在Vue实例的外部,全局可见
// 全局组件: 在多个Vue实例中均有效,所以它是全局的
// 以后推荐不要用了, 尽可能用:局部组件
Vue.component("button-inc", {
template: `
<div>
<button @click="count++">点赞: {{count}}</button>
</div>
`,
data() {
return {
count: 0,
};
},
});
new Vue({
el: "#root",
});
// 再创建一个vue实例,这个实例接管的是#root1的挂载点,根组件
new Vue({
el: "#root1",
});
</script>
十三、局部组件
<!-- 挂载点 -->
<div id="root">
<hello></hello>
</div>
<!-- <div id="root1">
<hello></hello>
</div> -->
<template id="hello">
<p>Hello {{site}}</p>
</template>
<script>
const hello = {
// 可以将大段的html字符串写到html文档的template标签中,并用id进行关联
template: "#hello",
data() {
return {
site: "php中文网",
};
},
};
// 局部组件必须声明在Vue的实例中
new Vue({
el: "#root",
components: {
// 可以同时声明多个局部组件
// hello: 组件名称,它的值是一个对象
// hello: {
// template: `<p>Hello {{site}}</p>`,
// data() {
// return {
// site: "php中文网",
// };
// },
// },
// 当属性值变量与属性名相同时,且在同一个作用域中则可以省去值变量名称
// hello: hello,
hello,
},
});
// new Vue({
// el: "#root1",
// });
</script>
十四、父组件与子组件传参
<div id="app">
<btn-inc :my-name="username" :my-count="count"></btn-inc>
</div>
<script>
// 父组件向子组件传参是通过自定义属性传参
const vm = new Vue({
el: document.querySelector("#app"),
data() {
return {
username: "为中国女足喝彩!!!",
count: 0,
};
},
// 局部组件
components: {
btnInc: {
props: ["myName", "myCount"],
template: `
<div>
<button @click="num++">点赞: {{num}}</button>
<span>{{myName}}</span>
</div>
`,
data() {
return {
num: this.myCount,
};
},
},
},
});
</script>
十五、子组件与父组件传参
<div id="app">
<btn-inc :my-name="username" :my-count="count" @click-count="handle"></btn-inc>
</div>
<script>
// 子组件向父组件传参是通过声明一个"同名事件"来实现
// 组件之间的传参,理论来说是"单向"的
const vm = new Vue({
el: document.querySelector("#app"),
data() {
return {
username: "为中国女足喝彩!!!",
count: 0,
};
},
// 局部组件
components: {
btnInc: {
props: ["myName", "myCount"],
template: `
<div>
<button @click="$emit('click-count', 1)">点赞: {{myCount}}</button>
<span>{{myName}}</span>
</div>
`,
},
},
methods: {
handle(value) {
console.log(value);
this.count += value;
this.username = "今天是我老猪的最后一课,不要忘了我";
},
},
});
// 总结 :
// 1. 父组件 ---> 子组件: 自定义属性 :name, :count
// 2. 子组件 ---> 父组件: 自定义事件方法:
// 子组件: $emit('父组件中的自定义方法', '附加参数值')
// 父组件: @父组件中的自定义方法="事件方法"
</script>
十六、基于锚点实现的路由模式
<!-- 选项卡 -->
<div class="container">
<!-- 导航 -->
<nav>
<a href="#/list1">国际新闻</a>
<a href="#/list2">中国新闻</a>
</nav>
<!-- 这里显示与导航对应的内容 -->
<div class="route-view"></div>
</div>
<script>
let list1 = `
<ul>
<li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
<li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
<li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
</ul>
`;
let list2 = `
<ul>
<li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
<li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
<li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
</ul>
`;
const routeView = document.querySelector(".route-view");
// console.log(window.location.href); // url信息
// hashchang: 监听url中的锚点的变化
window.addEventListener("hashchange", show);
// window.addEventListener("load", show);
// 推荐DOMContentLoaded 代替 load,因为它只要创建好dom树就可以触发了
window.addEventListener("DOMContentLoaded", show);
function show() {
console.log(location.hash);
switch (location.hash) {
case "#/list1":
routeView.innerHTML = list1;
return;
case "#/list2":
routeView.innerHTML = list2;
return;
default:
routeView.innerHTML = list1;
}
}
</script>
十七、vue 路由原理与实现
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>vue路由原理与实现</title>
<link rel="stylesheet" href="style.css" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 路由模块加载 -->
<script src="vue-router-dev/dist/vue-router.js"></script>
</head>
<body>
<!-- 选项卡 -->
<div class="container">
<!-- 导航 -->
<nav>
<!-- router-link: 就是a标签 -->
<!-- to : a 标签 中 的href -->
<router-link to="/list1">国际新闻</router-link>
<router-link to="/list2">国内新闻</router-link>
</nav>
<!-- 这里显示与导航对应的内容 -->
<router-view class="route-view"></router-view>
</div>
<script>
let list1 = {
template: `
<ul>
<li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
<li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
<li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
</ul>
`,
};
let list2 = {
template: `
<ul>
<li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
<li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
<li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
</ul>
`,
};
// 1. 创建路由对象
const router = new VueRouter({
// 路由配置
routes: [
// 每一个路由参数都是一个对象,每一个对象对应着一个路由
{ path: "/list1", component: list1 },
{ path: "/list2", component: list2 },
],
});
// 2. 注册路由
new Vue({
// el: ".container",
// 注册路由
router,
}).$mount(".container");
</script>
</body>
</html>
style.css 文件:
a {
text-decoration: none;
color: #555;
}
li {
list-style: none;
}
ul {
margin: 0;
padding: 1em;
}
.container {
width: 30em;
display: flex;
flex-flow: column nowrap;
}
nav {
height: 2em;
line-height: 2em;
padding: 0 2em;
background-color: skyblue;
display: flex;
}
nav a {
padding: 0 0.5em;
}
nav a:hover {
background-color: green;
color: white;
}
.route-view {
background-color: #efefef;
}
.route-view ul li {
line-height: 1.5em;
}
.route-view ul li a:hover {
color: coral;
}