VUE渐进式javacript框架, 一套拥有自己规则的语法,官网地址: https://cn.vuejs.org/,渐进式: 逐渐进步, 想用什么就用什么, 不必全都使用。库是方法的集合, 而框架是一套拥有自己规则的语法。
@vue/cli是Vue官方提供的一个全局模块包(得到vue命令), 此包用于创建脚手架项目.脚手架里面已经帮我们配置好了webpack
main.js 打包了APP.vue,然后又把相关的内容放到了index.html上让我们看到。
6. 关系总结如下:
module.exports = {
// ...其他配置
lintOnSave: false // 关闭eslint检查
}
export default {
data(){
return {
count: 1
}
},
// 2. 定义函数
methods: {
addFn(){ // this代表export default后{}对象, data和methods里的属性都直接挂在它身上
this.count++
},
}
}
<template>
<div>
<a @click="one" href="http://www.baidu.com">百度</a>
<hr />
<a @click="two(10, $event)" href="http://www.taobao.com">淘宝</a>
</div>
</template>
<script>
export default {
methods: {
// 1. 事件触发, 无传值, 可以直接获取事件对象是
one(e){
e.preventDefault()
},
// 2. 事件触发, 传值, 需要手动传入$event
two(num, e){
e.preventDefault()
}
}
};
</script>
<div>
<span>来自于: </span>
<!-- 下拉菜单要绑定在select上 -->
<select v-model="from">
<option value="北京市">北京</option>
<option value="南京市">南京</option>
<option value="天津市">天津</option>
</select>
</div>
<div>
<!-- (重要)
遇到复选框, v-model的变量值,正常情况下我们需要把变量定义为数组
非数组 - 关联的是复选框的checked属性
数组 - 关联的是复选框的value属性
-->
<span>爱好: </span>
<input type="checkbox" v-model="hobby" value="抽烟">抽烟
<input type="checkbox" v-model="hobby" value="喝酒">喝酒
<input type="checkbox" v-model="hobby" value="写代码">写代码
</div>
<div>
<span>性别: </span>
<input type="radio" value="男" name="sex" v-model="gender">男
<input type="radio" value="女" name="sex" v-model="gender">女
</div>
<div>
<span>自我介绍</span>
<textarea v-model="intro"></textarea>
</div>
</div>
<!-- v-if和v-else使用 -->
<p v-if="age >= 18">成年了</p>
<p v-else>未成年</p>
<div>
<!-- 语法1:
v-for="(值变量名, 索引变量名) in 目标结构"
口诀: 想要谁循环就放到谁身上
-->
<ul>
<li v-for="(item, index) in arr" :key="index">
{{ item }} ---- {{ index }}
</li>
</ul>
<!-- 语法2:
v-for="值变量名 in 目标结构"
-->
<ul>
<li v-for="obj in stuArr" :key="obj.id">
<span>{{ obj.name }}</span>
<span>{{ obj.sex }}</span>
<span>{{ obj.hobby }}</span>
</li>
</ul>
<!-- 语法3:(了解)
v-for="(value, key) in 对象"
-->
<div>
<p v-for="(value, key) in tObj" :key="value">
<span>{{ value }}</span>
=======
<span>{{ key }}</span>
</p>
</div>
<!-- 语法4: (了解)
v-for="变量名 in 固定数字"
从1开始遍历
-->
<div v-for="n in count" :key="n">{{ n }}</div>
</div>
<p>使用翻转过滤器: {{ msg | reverse }}</p>
<p :title="msg | toUp">鼠标长停</p>
<div>
<p>{{ num }}</p>
</div>
//与data同级别
export default {
data(){
return {
a: 10,
b: 20
}
},
// 注意: 计算属性和data属性都是变量-不能重名
// 注意2: 函数内变量变化, 会自动重新计算结果返回
computed: {
num(){
return this.a + this.b
}
}
}
<div>
<span>姓名:</span>
<input type="text" v-model="full">
</div>
export default {
computed: {
full: {
// 给full赋值触发set方法
set(val){
console.log(val)
},
// 使用full的值触发get方法
get(){
return "无名氏"
}
}
}
}
/* 语法:
watch: {
变量名 (newVal, oldVal){
// 变量名对应值改变这里自动触发
}
}
*/
watch: {
// newVal: 当前最新值
// oldVal: 上一刻值
name(newVal, oldVal){
console.log(newVal, oldVal);
}
}
/*语法:
对象名: {
handler(newVal, oldVal){
},
deep: true, // 深度侦听(对象里面层的值改变)
immediate: true // 立即侦听(网页打开handler执行一次)
}
}
*/
watch: {
user: {
handler(newVal, oldVal){
// user里的对象
console.log(newVal, oldVal);
},
deep: true,
immediate: true
}
}
组件是可复用的 Vue 实例, 封装标签, 样式和JS代码。组件化 :封装的思想,把页面上可重用的部分封装为 组件,从而方便项目的 开发 和 维护。 一个页面, 可以拆分成一个个组件,一个组件就是一个整体, 每个组件可以有自己独立的 结构 样式 和 行为(html,css和js)
main.js:
import 组件对象 from 'vue文件路径'
Vue.component("组件名",组件对象)
使用的时候:
直接<组件名></组件名>
// 1. 创建组件 - 文件名.vue
// 2. 引入组件
import Pannel from './components/Pannel_1'
export default {
// 3. 局部 - 注册组件
/*
语法:
components: {
"组件名": 组件对象
}
*/
components: {
PannelL: Pannel
//如果同名可以简写成一个即可
}
}
使用:
<PannelL></PannelL>
<!--
目标: 父(App.vue) -> 子(MyProduct.vue) 分别传值进入
需求: 每次组件显示不同的数据信息
步骤(口诀):
1. 子组件 - props - 变量 (准备接收)
2. 父组件 - 传值进去
-->
子文件:
<template>
<div class="my-product">
<h3>标题: {{ title }}</h3>
<p>价格: {{ price }}元</p>
<p>{{ intro }}</p>
</div>
</template>
<script>
export default {
props: ['title', 'price', 'intro']
}
</script>
父亲文件:
<template>
<div>
<MyProduct v-for="obj in list" :key="obj.id"
:title="obj.proname"
:price="obj.proprice"
:intro="obj.info"
></MyProduct>
</div>
</template>
<script>
// 目标: 循环使用组件-分别传入数据
// 1. 创建组件
// 2. 引入组件
import MyProduct from './components/MyProduct'
export default {
data() {
return {
list: [
{
id: 1,
proname: "超级好吃的棒棒糖",
proprice: 18.8,
info: "开业大酬宾, 全场8折",
},
{
id: 2,
proname: "超级好吃的大鸡腿",
proprice: 34.2,
info: "好吃不腻, 快来买啊",
},
{
id: 3,
proname: "超级无敌的冰激凌",
proprice: 14.2,
info: "炎热的夏天, 来个冰激凌了",
},
],
};
},
// 3. 注册组件
components: {
// MyProduct: MyProduct
MyProduct
}
};
</script>
空白组件/index.js://注意名字,通常是eventBus文件夹
import Vue from 'vue'
// 导出空白vue对象
export default new Vue()
传值文件:
import eventBus from '../EventBus'
export default {
methods: {
subFn(){
eventBus.$emit("send", 传递参数) // 跨组件
}
}
}
接受文件:
// 1. 引入空白vue对象(EventBus)
// 2. 接收方 - $on监听事件
import eventBus from "../EventBus";
export default {
// 3. 组件创建完毕, 监听send事件(事件名,自定义)
created() {
eventBus.$on("send", (参数) => {
......
});
},
};
axios 是一个专门用于发送ajax请求的库(用于前端异步请求后端的技术),官网: http://www.axios-js.com/, axios 底层还是原生js实现, 内部通过Promise封装的,axios()-原地得到的是Promise对象
网络 ->xhr 进行调试
// 1. 下载axios
// 2. 引入axios
// 3. 发起axios请求
import axios from "axios";
// 4. 全局配置
axios.defaults.baseURL = "http://123.57.109.30:3006"//便于后续工作修改
export default {
data() {
return {
bName: "",
bookObj: { // 参数名提前和后台的参数名对上-发送请求就不用再次对接了
bookname: "",
author: "",
publisher: ""
}
};
},
methods: {
findFn() {
axios({
url: "/api/getbooks",
method: "GET",
params: { // 都会axios最终拼接到url?后面
bookname: this.bName
}
}).then(res => {
console.log(res);
})
},
sendFn() {
axios({
url: "/api/addbook",
method: "POST",
data: {
appkey: "7250d3eb-18e1-41bc-8bb2-11483665535a",
...this.bookObj,
//等同于下面
// bookname: this.bookObj.bookname,
// author: this.bookObj.author,
// publisher: this.bookObj.publisher
},//参数名和值, 会被axios拼接到请求体里
});
},
methods: {
btn(){
this.count++; // vue监测数据更新, 开启一个DOM更新队列(异步任务)
// console.log(this.$refs.myP.innerHTML); // 0
// 原因: Vue更新DOM异步
// 解决: this.$nextTick()
// 过程: DOM更新完会挨个触发$nextTick里的函数体
this.$nextTick(() => {
console.log(this.$refs.myP.innerHTML); // 1
})
}
}
组件内部:
<template>
<div class="my-header" :style="{backgroundColor: background, color}">{{ title }}</div>
</template>
export default {
props: {
background: String, // 外部插入此变量的值, 必须是字符串类型, 否则报错
color: {
type: String, // 约束color值的类型
default: "#fff" // color变量默认值(外部不给 我color传值, 使用默认值)
},
title: {
type: String,
required: true // 必须传入此变量的值
}
arr:{
// 自定义校验规则
validator(value) {
// value就是接到数组arr
if (value.length >= 2 && value.length <= 5) {
return true; // 符合条件就return true
} else {
console.error("数据源必须在2-5项");
return false;
}
},
}
}
调用者:
<MyHeader title="购物车案例"></MyHeader>
main.js:
/ 目标: 请求数据 - 打印
// 1. 下载axios库, main.js - 全局绑定属性 (确保任意.vue文件可以都访问到这个axios方法)
import axios from 'axios'
// 2. 基础地址
axios.defaults.baseURL = "https://www.escook.cn"
// 3. axios方法添加到Vue的原型上
Vue.prototype.$axios = axios
实现一个页面动态切换不同的界面,两个的话可以考虑v-if,但是多个不行太麻烦,我们可以采用下述的方法 component是一个内置组件,is是固定的属性,依赖 “is” 的值来决定渲染哪一个组件,但是频繁的切换会导致组件频繁创建和销毁,性能不高,因此可以将其用keep-alive标签包起来,如下:
<template>
<div>
<button @click="comName = 'UserName'">账号密码填写</button>
<button @click="comName = 'UserInfo'">个人信息填写</button>
<p>下面显示注册组件-动态切换:</p>
<div style="border: 1px solid red;">
<keep-alive>
<component :is="comName"></component>
</keep-alive>
</div>
</div>
</template>
<script>
// 目标: 动态组件 - 切换组件显示
// 场景: 同一个挂载点要切换 不同组件 显示
// 1. 创建要被切换的组件 - 标签+样式
// 2. 引入到要展示的vue文件内, 注册
// 3. 变量-承载要显示的组件名
// 4. 设置挂载点
// 5. 点击按钮-切换comName的值为要显示的组件名
import UserName from '../components/01/UserName'//被切换的组件一
import UserInfo from '../components/01/UserInfo'//被切换的组件二
export default {
data(){
return {
comName: "UserName"
}
},
components: {
UserName,
UserInfo
}
}
</script>
export default {
// 组件缓存下 - 多了2个钩子函数
activated(){//获得激活状态
console.log("02-UserName-激活");
},
deactivated(){//获得激活状态
console.log("02-UserName-失去激活");
}
}
使用的组件,里面的内容会替代slot标签位置,多个插槽可以使用下述方法:
1. slot使用name属性区分名字
2. template配合v-slot:名字来分发对应标签
<Pannel>
<template v-slot:title>
<h4>芙蓉楼送辛渐</h4>
</template>
<template v-slot:content>
<img src="../assets/mm.gif" alt="">
<span>我是内容</span>
</template>
</Pannel>
<Pannel>
<template #title>//v-slot:可以简化成#
<span style="color: red;">我是标题</span>
</template>
<template #content>
<p>寒雨连江夜入吴,</p>
<p>平明送客楚山孤。</p>
<p>洛阳亲友如相问,</p>
<p>一片冰心在玉壶。</p>
</template>
</Pannel>
<Pannel></Pannel>
被使用的Pannel组件,如果没有传入数值的时候,会显示默认的内容:
<template>
<slot name="title">默认显示的内容</slot>
<slot name="content">默认显示的内容</slot>
</template>
自己定义指令:
//全局注册,在main.js
// 全局指令 - 到处"直接"使用
// 目标: 自定义指令传值(如果没有值的时候,就只写一个参数,其是获取当前标签)
Vue.directive('color', {
inserted(el, binding) {//inserted方法 - 指令所在标签, 被插入到网页上触发(一次)
el.style.color = binding.value
},
update(el, binding) {//update方法 - 指令对应数据/标签更新时, 此方法执行
el.style.color = binding.value
}
})
//局部注册
<template>
<div>
<input type="text" v-focus>
<p v-color="colorStr">修改文字颜色</p>
</div>
</template>
export default {
data(){
return {
colorStr: 'red'
}
},
directives: {
focus: {
inserted(el){
el.focus()
}
}
}
}
前端路由:路径和组件的映射关系,主要使用vue-router这个第三方包,单页面应用(SPA): 所有功能在一个html页面上实现
前端路由作用: 实现业务场景切换。
⚫ 优点:整体不刷新页面,用户体验更好。 数据传递容易, 开发效率高
⚫ 缺点: 开发成本高(需要学习专门知识)。 首次加载会比较慢一点。不利于seo
main.js:
import Vue from 'vue'
import App from './App.vue'
import Find from '@/views/Find' // @是src的绝对地址
import My from '@/views/My'
import Part from '@/views/Part'
import NotFound from '@/views/NotFound'
// 目标: vue-router基础使用
// 1. 下载vue-router (yarn add vue-router)
// 2. 引入
import VueRouter from 'vue-router'
// 3. 注册全局组件
Vue.use(VueRouter)
// 4. 规则数组
const routes = [
//路径从上到下逐一配对
//重定向,默认打开的路径
{
path: "/", // 默认hash值路径
redirect: "/find" // 重定向到/find
// 浏览器url中#后的路径被改变成/find-重新匹配规则
},
{
path: "/find",
name: "Find",
component: Find,
},
{
path: "/my",
name: "My",
component: My
},
{
path: "/part",
name: "Part",
component: Part
},
{
path: "/part/:username", // 有:的路径代表要接收具体的值
component: Part
},
// 404在最后(规则是从前往后逐个比较path)
{
path: "*",
component: NotFound
}
]
// 5. 生成路由对象
const router = new VueRouter({
routes,// routes是固定key(传入规则数组)
// mode: "history" // 默认不写是"hash"
})
Vue.config.productionTip = false
// 6. 路由对象注入到vue实例中, this可以访问$route和$router
new Vue({
router,
render: h => h(App),
}).$mount('#app')
在使用的App.vue文件中:
<template>
<div>
<div class="footer_wrap">
<a href="#/find">发现音乐</a>
<a href="#/my">我的音乐</a>
<a href="#/part">朋友</a>
</div>
<div class="top">
<!-- 7. 设置挂载点-当url的hash值路径切换, 显示规则里对应的组件到这 -->
<router-view></router-view>
</div>
</div>
</template>
App.vue文件中:
<template>
<div>
<div class="footer_wrap">
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/part">朋友</router-link>
</div>
<div class="top">
<router-view></router-view>
</div>
</div>
</template>
<script>
// 目标: 声明式导航 - 基础使用
// 本质: vue-router提供的全局组件 "router-link"替代a标签
// 1. router-link 替代a标签
// 2. to属性 替代href属性
// 好处: router-link自带高亮的类名(激活时类名)
// 3. 对激活的类名做出样式的编写
export default {};
</script>
<style scoped>
.footer_wrap {
position: fixed;
left: 0;
top: 0;
display: flex;
width: 100%;
text-align: center;
background-color: #333;
color: #ccc;
}
.footer_wrap a {
flex: 1;
text-decoration: none;
padding: 20px 0;
line-height: 20px;
background-color: #333;
color: #ccc;
border: 1px solid black;
}
.footer_wrap a:hover {
background-color: #555;
}
.top {
padding-top: 62px;
}
/*激活时样式 */
.footer_wrap .router-link-active{
color: white;
background: black;
}
</style>
第一种:$route.query.参数名 接收参数
<router-link to="/part?name=小传">朋友-小传{{$route.query.name}}</router-link>
第二种: $route.params.参数名 接收参数
<router-link to="/part/小智">朋友-小智{{$route.params.username}}</router-link>
main.js
{
path: "/part/:username", // 有:的路径代表要接收具体的值
component: Part
},
const router = new VueRouter({
routes,// routes是固定key(传入规则数组)
// mode: "history" // 默认不写是"hash"
})
APP.VUE:
<template>
<div>
<div class="footer_wrap">
<span @click="btn('/find', 'Find')">发现音乐</span>
<span @click="btn('/my', 'My')">我的音乐</span>
<span @click="btn('/part', 'Part')">朋友</span>
</div>
<div class="top">
<router-view></router-view>
</div>
</div>
</template>
<script>
// 目标: 编程式导航 - js方式跳转路由
// 语法:
// this.$router.push({path: "路由路径"})
// this.$router.push({name: "路由名"})
// 注意:
// 虽然用name跳转, 但是url的hash值还是切换path路径值
// 场景:
// 方便修改: name路由名(在页面上看不见随便定义)
// path可以在url的hash值看到(尽量符合组内规范)
export default {
//方式一:
// methods:{
// btn(targetPath){
// // 方式1: path跳转
// this.$router.push({
// path: targetPath,
// })
// }
// }
//方式二:需在main.js路由那里给路径定义name值
methods: {
btn(targetPath, targetName){
this.$router.push({
name: targetName
})
}
}
};
</script>
main.js– 继续配置2级路由
⚫ 一级路由path从/开始定义
⚫ 二级路由往后path直接写名字, 无需/开头
⚫ 嵌套路由在上级路由的children数组里编写路由信息对象
记得引入页面组件
{
path: "/find",
name: "Find",
component: Find,
children: [
{
path: "recommend",
component: Recommend
},
{
path: "ranking",
component: Ranking
},
{
path: "songlist",
component: SongList
}
]
},
然后再对应的find组件中定义路由
<div class="nav_main">
<router-link to="/find/recommend">推荐</router-link>
<router-link to="/find/ranking">排行榜</router-link>
<router-link to="/find/songlist">歌单</router-link>
</div>
<div style="1px solid red;">
<router-view></router-view>
</div>
//目标:路由守卫
//场景:当你要对路由权限判断时
语法:router .beforeEach ((to,from,next)=>{//路由跳转"之前"先执行这里,决定是否跳 转})
参数1:要跳转到的路由(目标路由对象信息)
参数2:从哪里跳转的路由(来源 路由对象信息)
参数3:函数体-next()才会让路由正常的跳转切换,
next(false )在原地停留,next("强制修 改到另一个路由路径上")
//注意:如果不调用next,页面留在原地工
Vant是一个轻量、可靠的移动端 Vue 组件库, 开箱即用(引用下载的时候注意看清是vue的版本)
网址:https://vant-contrib.gitee.io/vant/#/zh-CN/
解决跨域,可以在本地写一个服务器(node.js)可以去百度上去找对应的API的代码,再用服务器去访问跨域的资源,因为跨域是针对浏览器之间的,服务器是可以进行访问的
详见笔记:http://zhoushugang.gitee.io/erabbit-client-pc-document/
尝试使用原生的js实现VUE的功能,便于对基本原理的掌握,详见笔记https://cp_fe.gitee.io/course-advance/#/