"对A,我还剩一张牌啦!"
"呃。。。要不起"
???????
周六(12-03),早上刷到印度的三相神。梵天,毗湿奴,湿婆,看来很多个相关的视频介绍,结果睡觉(不管是在床上还是在桌子上)的时候,脑子里都是创造与毁灭。。。
周末(12-11),放纵得看了一天de恐怖末日小说(林叔护体),后来我知道为啥自己管不住自己了。少了一样东西,所以有了那样东西,我才能认真去完成这篇博客
周末(12-17,12-18)俩天被洗脑了,看了好几个主播的“鸭鹅杀”。脑子里总是那一句:“叼得一发言:叼的一认为.....,对是不对”
Vue CLI
Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统(脚手架)
?参考官网:介绍 | Vue CLI?
CLI (@vue/cli ) 是一个全局安装的 npm 包,提供了终端里的?vue ?命令。它可以通过?vue create ?快速搭建一个新项目,或者直接通过?vue serve ?构建新想法的原型。你也可以通过?vue ui ?通过一套图形化界面管理你的所有项目。
1.安装vue cli?
在这之前,确保安装了Node(推荐 v10 以上)
npm install -g @vue/cli
验证它是否安装成功
vue --version
如下:则安装成功
2.创建项目
vue create 项目名
vue create vue-xiaoyumao
选择vue2 ,回车,完成后就会在桌面生成一个文件夹
3.项目目录结构分析
使用VsCode打开
main.js?是?npm run serve 的开端
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
index.html
<!DOCTYPE html>
<html lang="">
<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">
<!--配置页签图标-->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!--配置网页标题-->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!--当浏览器不支持js时noscript中的元素就会被渲染 -->
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<!--容器-->
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
4.npm run serve 启动项目
打开终端,执行 npm run serve 命令
补充:npm run build 命令会生成最纯粹的js,css,html
最后的效果
ctrl+单击网址(哪个都行),出现如下页面就欧克了
接下来换成我们自己写的组件进行渲染
新建pages目录,下面建2个.vue文件
新增Bedroom.Vue
<template>
<div>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Bedroom",
data() {
return {
msg: "欢迎来到卧室:你要睡觉吗",
};
},
};
</script>
新增Kitchen.vue
<template>
<div>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Kitchen",
data() {
return {
msg: '欢迎来到厨房:你要做饭吗',
};
},
};
</script>
修改App.vue,引入我们自定义的2个组件并注册使用。如下:
<template>
<div id="app">
<Bedroom />
<Kitchen />
</div>
</template>
<script>
import Bedroom from "./pages/Bedroom.vue";
import Kitchen from "./pages/Kitchen.vue";
export default {
name: "App",
components: {
Bedroom,
Kitchen,
},
};
</script>
页面打开后,渲染效果如下:
组件component
1、组件概念
模块化(只针对js文件)请参考:
22-08-02 西安 尚医通(02)Vscode、ES6、nodejs、npm、Bable转码器、js模块化、webpack_£小羽毛的博客-CSDN博客_尚医通 nodejs 实现
组件的定义 实现应用中局部功能代码(js,css,html)和资源的集合
从上图得出2点结论:
1.所有的组件都要听大哥vm的指挥
2.组件可以产生嵌套
2、组件嵌套
在单文件组件Bedroom.vue种引入Kitchen组件并使用:
<!--Bedroom组件内容-->
<template>
<div>
<p>{{ msg }}</p>
<kitchen/>
</div>
</template>
<script>
import Kitchen from './Kitchen.vue';
export default {
components: { Kitchen },
// eslint-disable-next-line vue/multi-word-component-names
name: "Bedroom",
data() {
return {
msg: "欢迎来到卧室:你要睡觉吗",
};
},
};
</script>
---------------------------------
<!--Kitchen组件内容-->
<template>
<div>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Kitchen",
data() {
return {
msg: '欢迎来到厨房:你要做饭吗',
};
},
};
</script>
打开vue开发者工具,直观效果"组件嵌套"
页面显示效果:
光标放在Bedroom标签:
?光标放在Kitchen标签上:
实际上在源码里:
div元素没有特定的含义,一般和CSS一同使用。用于对内容块设置样式属性。浏览器会在其前后显示折行。
VM非常省心,只管理app组件(约定)
3、VueComponent(简称VC)
VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。 Vue的实例对象,以后简称vm。
组件本质 1、Bedroom组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。 2、我们只需要写<Bedroom/>或<Bedroom></Bedroom>,Vue解析时会帮我们创建Bedroom组件的实例对象,即Vue帮我们执行的:new VueComponent(options).
3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!
关于this指向: (1)组件配置中: data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是vc【VueComponent实例对象】
(2)new Vue(options)配置中: data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是【Vue实例对象】
vm管理着vc
vm能通过el决定为哪个容器服务,vc不行,只能跟着大哥vm混
vc中,data必须是一个函数,vm中可以是data函数也可以是data对象
组件通信
1.父传子props
让组件接收外部传过来的数据
接收数据: 第一种方式(只接收):props:['name'] 第二种方式(限制数据类型):props:{name:String} 第三种方式(限制类型、限制必要性、指定默认值): props:{ ? ? name:{ ? ? type:String, //类型 ? ? ? ? required:true, //必要性 ? ? ? ? default:'JOJO' //默认值 ? ? } }
2.子传父this.$emit
触发自定义事件:this.$emit('atguigu',数据)
3.this.$refs
应用在html标签上获取的是真实DOM元素,应用在组件标签上获取的是组件实例对象(vc) 使用方式:
打标识:<h1 ref="xxx"></h1> 或 <School ref="xxx"></School>
获取:this.$refs.xxx
SlOT插槽
1.默认插槽
先弄一个基本效果
创建Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<ul>
<li v-for="(item,index) in listData" :key="index">{{item}}</li>
</ul>
</div>
</template>
<script>
export default {
name: "Category",
props:['listData','title']
};
</script>
<style>
.category {
background-color: aqua;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: burlywood;
}
</style>
在App.vue中引入并使用Category.vue
<template>
<div id="app" class="container">
<Category title="中立阵营" :listData="zhongli" />
<Category title="好人阵营" :listData="haoren" />
<Category title="狼人阵营" :listData="lang" />
</div>
</template>
<script>
import Category from "./components/Category.vue";
export default {
name: "App",
components: {
Category,
},
data() {
return {
zhongli: ["呆呆鸟", "猎鹰", "秃鹫", "鸽子", "鹈鹕"],
haoren: [
"加拿大鹅",
"观鸟者",
"鹅",
"网红",
"星界",
"警长",
"正义使者",
"模仿",
"侦探",
"通灵",
],
lang: [
"刺客",
"间谍",
"食鸟鸭",
"专业杀手",
"承办丧葬者",
"身份窃贼",
"忍者",
"爆炸王",
],
};
},
};
</script>
<style >
.container {
display: flex;
justify-content: space-around;
}
</style>
页面效果:
接着我们开始引入插槽
在Category.vue中做改变,加<slot>标签
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽,等别的组件填充 -->
<slot>我是插槽的默认值,当没有传递具体结构,我会出现</slot>
</div>
</template>
<script>
export default {
name: "Category",
props:['title']
};
</script>
<style>
.category {
background-color: aqua;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: burlywood;
}
</style>
在App.vue中使用<Category>时给插槽传入具体的结构和数据
<template>
<div id="app" class="container">
<Category title="中立阵营" :listData="zhongli">
<img src="./static/image/zhongli.jpg" />
</Category>
<Category title="好人阵营" :listData="haoren">
<ul>
<li v-for="(item, index) in haoren" :key="index">{{ item }}</li>
</ul>
</Category>
<Category title="狼人阵营" :listData="lang">
<img src="./static/image/lang.jpg" />
</Category>
</div>
</template>
<script>
import Category from "./components/Category.vue";
export default {
name: "App",
components: {
Category,
},
data() {
return {
zhongli: ["呆呆鸟", "猎鹰", "秃鹫", "鸽子", "鹈鹕"],
haoren: [
"加拿大鹅",
"观鸟者",
"鹅",
"网红",
"星界",
"警长",
"正义使者",
"模仿",
"侦探",
"通灵",
],
lang: [
"刺客",
"间谍",
"食鸟鸭",
"专业杀手",
"承办丧葬者",
"身份窃贼",
"忍者",
"爆炸王",
],
};
},
};
</script>
<style >
.container {
display: flex;
justify-content: space-around;
}
img{
width:100%
}
</style>
页面效果:
2.具名插槽
在Category.vue中做改变,name属性给插槽起名字
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽,等别的组件填充 -->
<slot name="center">我是插槽的默认值,当没有传递具体结构,我会出现1</slot>
<slot name="footer">我是插槽的默认值,当没有传递具体结构,我会出现2</slot>
</div>
</template>
在App.vue中,slot属性指定填充哪个插槽
<template>
<div id="app" class="container">
<Category title="中立阵营" :listData="zhongli">
<img slot="center" src="./static/image/zhongli.jpg" />
<a slot="footer" href="">点我查看更多</a>
</Category>
<Category title="好人阵营" :listData="haoren">
<ul slot="center">
<li v-for="(item, index) in haoren" :key="index">{{ item }}</li>
</ul>
<div slot="footer">
<a href="">好人阵营</a>
<h2>欢迎加入好人阵营</h2>
</div>
</Category>
<Category title="狼人阵营" :listData="lang">
<img slot="center" src="./static/image/lang.jpg" />
<template slot="footer">
<a href="">狼人阵营</a>
<h4>欢迎来到我们狼人阵营</h4>
</template>
</Category>
</div>
</template>
效果如下:
如果使用template包裹,可以有新的写法
<!-- 旧的写法如下 -->
<Category title="狼人阵营" :listData="lang">
<img slot="center" src="./static/image/lang.jpg" />
<template slot="footer">
<a href="">狼人阵营</a>
<h4>欢迎来到我们狼人阵营</h4>
</template>
</Category>
新的写法:注意v-slot只能写在组件的<template>
<Category title="狼人阵营" :listData="lang">
<img slot="center" src="./static/image/lang.jpg" />
<template v-slot:footer>
<a href="">狼人阵营</a>
<h4>欢迎来到我们狼人阵营</h4>
</template>
</Category>
区别:
3.作用域插槽
数据和结构不在一块
数据放在Category.vue中
<template>
<div class="category">
<h3>{{title}}</h3>
<!-- 定义一个插槽,等别的组件填充 sanxianshen会传给插槽的使用者-->
<slot :sanxianshen="sanxianshen">我是插槽的默认内容</slot>
</div>
</template>
<script>
export default {
name: "Category",
props:['title'],
data(){
return{
sanxianshen: ["大天湿婆", "梵天", "毗湿奴"]
}
}
};
</script>
<style>
.category {
background-color: aqua;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: burlywood;
}
</style>
在App.vue中接收到插槽传上来的数据。使用template包裹并且使用scope属性
<template>
<div id="app" class="container">
<Category title="印度三相神">
<!-- 必须要用template包裹才能拿到插槽给的数据 -->
<template scope="India">
{{India}}
</template>
</Category>
</div>
</template>
<script>
import Category from "./components/Category.vue";
export default {
name: "App",
components: {
Category,
},
data() {
return {}
},
};
</script>
<style >
.container {
display: flex;
justify-content: space-around;
}
img {
width: 100%;
}
</style>
页面效果:
?在App.vue中展示数据的时候,使用不同的样式展示(无序列表,有序列表,p标签)?
<template>
<div id="app" class="container">
<Category title="印度三相神">
<!-- 必须要用template包裹才能拿到插槽给的数据 -->
<template scope="India">
<ul>
<li v-for="(item, index) in India.sanxianshen" :key="index">{{ item }}</li>
</ul>
</template>
</Category>
<Category title="印度三相神">
<template scope="India">
<ol>
<li v-for="(item, index) in India.sanxianshen" :key="index">{{ item }}</li>
</ol>
</template>
</Category>
<Category title="印度三相神">
<template scope="India">
<p v-for="(item, index) in India.sanxianshen" :key="index">{{ item }}</p>
</template>
</Category>
</div>
</template>
当然还可以使用ES6的解构赋值,看起来更好看
<template>
<div id="app" class="container">
<Category title="印度三相神">
<!-- ES6解构赋值 -->
<template scope="{sanxianshen}">
<ul>
<li v-for="(item, index) in sanxianshen" :key="index">{{ item }}</li>
</ul>
</template>
</Category>
<!-- 使用slot-scope,是新的api -->
<Category title="印度三相神">
<template slot-scope="{sanxianshen}">
<ol>
<li v-for="(item, index) in sanxianshen" :key="index">{{ item }}</li>
</ol>
</template>
</Category>
</div>
</template>
路由Vue Router
1.路由route和路由器router概述
路由route是一组key-value的对应关系;多个路由需要经过路由器的管理
对应在vue中就是 路径与组件的映射成为路由
路由器router时时刻刻检测路径变化
2.使用路由后有哪些变化
vue-router是vue的一个插件库,专门用来实现spa应用(单页面web应用),即整个页面只有一个完成的页面。
vue-router相比之前的多页面(多个html)的区别
1.点击页面中的导航链接不会刷新页面
2.做页面的局部更新,数据需要通过ajax请求获取
3.创建路由器/引入路由器
npm install vue-router --save
后续可能会报错:vue_router__WEBPACK_IMPORTED_MODULE_2__.default is not a constructor
解决办法:卸载原本的vue-router,指定安装版本
npm uninstall vue-router
npm install vue-router@3
router/index.js创建路由器
新建router文件夹
在index.js中写入如下内容?
//该文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
//创建并暴露一个路由器
export default new VueRouter({
routes: [
{
path: "/bedroom",
component: () => {
import("../pages/Bedroom");
},
},
{
path: "/kitchen",
component: () => {
import("../pages/Kitchen");
},
},
],
});
main.js配置路由器
在main.js中添加内容如下
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入VueRouter
import VueRouter from 'vue-router'
//引入路由器
import router from './router'
//关闭vue的生产提示
Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter);
new Vue({
router:router,
render: h => h(App),
}).$mount('#app')
4.router-link/rouer-view使用路由器
router-link实现点击不同的导航,路径发生变化
<router-link>:该标签是一个vue-router中已经内置的组件,他会被渲染成一个<a>标签。
router-link属性介绍
to:用于指定跳转的路径
tag:tag可以指定<router-link>之后渲染成什么组件,比如我们下面的代码会被渲染成一个<li>元素,而不是<a> 。 如:<router-link to='/home' tag='li'>
<router-view>:该标签会根据当前的路径,动态渲染出不同的组件。
在APP.vue中,修改为如下内容
<template>
<div id="app">
<div>
<!--vue中借助router-link 实现路由的切换 -->
<router-link to="/bedroom">卧室</router-link>
<br/>
<router-link to="/kitchen">厨房</router-link>
<!--指定组件的呈现位置-->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: "App",
components: {},
};
</script>
在页面的效果:
点击卧室
点击厨房:
5.编程式路由导航
不借助<router-link>实现路由跳转,让路由跳转更加灵活
this.$router.push({
})
this.$router.replace({
})
this.$router.forward()//前进
this.$router.back()//后退
this.$router.go()
路由缓存
路由切走之后,再切回来。数据就没了,因为组件会销毁和重新挂载
6.路由器的2种工作模式
路由器的默认工作模式是hash
hash模式
1.对于一个url来说,什么是hash值?#及其后面的内容就是hash值。
2.hash值不会包含在HTTP请求中,即:hash值不会随着http请求发给服务器。
3.hash模式
1.地址中永远带着#号,不美观。
2.若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
3.兼容性较好
history模式 1.地址干净,美观。 2.兼容性和hash模式相比略差。 3.应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
VueX 多组件共享数据
VueX不属于任何一个组件,它不在app里
vuex定义:专门在Vue中实现集中式状态(数据)管理的一个vue插件
1、纯vue版实现求和案例
创建组件Count.vue
<template>
<div>
<h1>当前求和为:{{ sum }}</h1>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前求和为奇数再加</button>
<button @click="incrementWait">等一等再加</button>
</div>
</template>
<script>
export default {
name: "Count",
data() {
return {
n: 1, //用户选择的数字
sum: 0, //当前的和
};
},
methods: {
increment() {
this.sum+=this.n
},
decrement() {
this.sum-=this.n
},
incrementOdd() {
if(this.sum%2){
this.sum+=this.n
}
},
incrementWait() {
setTimeout(() => {
this.sum+=this.n
}, 500);
},
},
};
</script>
<style>
button {
margin-left: 5px;
}
</style>
在App.vue中引入Count.vue
<template>
<div id="app">
<count />
</div>
</template>
<script>
import Count from "./components/Count.vue";
export default {
name: "App",
components: {
Count,
},
};
</script>
页面效果:
说实话,我写完觉得也没啥大的毛病啊,看看vuex能做什么优化呢
2、Vuex工作原理图
Backend API:不知道加几去action后端问问,要是知道加几可以vc直接commit
工作对象
state:状态,用于存储对象数据 Actions:行为,用于保存方法的行为,可以包含异步操作 Mutations:转变,用于提交行为的结果,不可以包含异步操作 Getters:类似于在store中的计算属性 Modules:模块,将store分割成不同的模块,每个模块有自己的state、actions、mutations
store.dispatch
store.commit
state、Actions、Mutations这三个都要经过store管理
工作原理
Render:vue组件中可以读取state中的数据(对应工作原理图中的①这一条线) Dispatch:调用store中的dispatch方法,由vue组件派发给Actions执行,Actions可以继续给自身派发,也可以调用异步方法(Backend API)(对应工作原理图中的②这一条线) Commit:调用store中的commit方法,由Actions提交给Mutations执行,也可以直接由组件提交(对应工作原理图中的③⑤这两条线) Mutate:Actions、Mutations中更改state中的数据,不需要手动执行,由api直接调用。一般是Mutations调用,在此处调用,可以被开发者工具(Devtools)直接监控,由Actions调用时,不被监控。(对应工作原理图中的④⑥这两条线)
3、创建并引入store
因为我们使用的是vue2,对应就要安装vuex3
npm i vuex@3
创建store目录,并在其下创建index.js
index.js文件内容如下
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//使用插件
Vue.use(Vuex)
//准备actions,用于响应组件中的动作
const actions={}
//准备mutations,用于操作数据
const mutations={}
//准备state,用于存储数据
const state={}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
============
在main.js引入vuex插件,并在创建vm时传入store
import Vue from 'vue'
import App from './App.vue'
//引入store
import store from './store'
//关闭vue的生产提示
Vue.config.productionTip = false
//创建vm
const vm=new Vue({
store,
render: h => h(App),
}).$mount('#app')
console.log(vm)
打开控制台就可以看到$store
4、使用Vuex对求和案例改造
改造store目录下的index.js
//该文件用于创建vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//使用插件
Vue.use(Vuex)
//准备actions,用于响应组件中的动作
const actions={
'jia':function(context,value){
//context上下文对象,可以理解为mini版store
console.log('actions中的jia被调用了',context,value)
context.commit('JIA',value)
},
'jian':function(context,value){
//context上下文对象,可以理解为mini版store
console.log('actions中的jian被调用了',context,value)
context.commit('JIAN',value)
}
}
//准备mutations,用于操作数据
const mutations={
'JIA':function(state,value){
console.log('mutations中的JIA被调用了',state,value)
state.sum+=value
},
'JIAN':function(state,value){
console.log('mutations中的JIAN被调用了',state,value)
state.sum-=value
}
}
//准备state,用于存储数据
const state={
sum:0
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
======改造Conut.vue
<template>
<div>
<!--模板中可以直接使用vc中的数据,不用写this.-->
<h1>当前求和为:{{ $store.state.sum }}</h1>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前求和为奇数再加</button>
<button @click="incrementWait">等一等再加</button>
</div>
</template>
<script>
export default {
name: "Count",
data() {
return {
n: 1//用户选择的数字
};
},
methods: {
increment() {
this.$store.dispatch('jia',this.n)
},
decrement() {
this.$store.dispatch('jian',this.n)
},
incrementOdd() {
if(this.$store.state.sum%2){
this.$store.dispatch('jia',this.n)
}
},
incrementWait() {
setTimeout(() => {
this.$store.dispatch('jia',this.n)
}, 500);
},
},
};
</script>
<style>
button {
margin-left: 5px;
}
</style>
此时效果就和原来的一样了,每个按钮都生效
可以做优化,没有业务逻辑可以跳过actions,在组件里使用commit方法而不是dispatch。
5、store中的getters配置项
当state中的数据需要经过加工后再使用,可以使用getters加工(适合逻辑复杂并需要被复用)
getters配置项?
在Count.vue中使用
<h1>当前求和为:{{ $store.state.sum }}</h1>
<h1>当前求和放大10倍为:{{ $store.getters.bigSum }}</h1>
|