https://www.bilibili.com/video/BV194411T79m?p=6
在完成组件自身的设计之后,还需要将组件注册到需要应用的位置,一般分为2个步骤:
1.import
2.components:{}
<script>
import cmpHeader from "@/components/cmpHeader"
import cmpFooter from "@/components/cmpFooter"
export default {
components:{cmpHeader,cmpFooter}
}
</script>
如果该组件被注册成了路由,还要写路由配置:
import login from "@/login"
import reg from "@/reg"
export default new Router({
routes: [
{
path: '/',
name: 'login',
component: login
},
{
path: '/reg',
name: 'reg',
component: reg
},
]
})
当然,组件本身也最好做些记录,比如:
<script>
export default {
name:"cmpHeader"
}
</script>
其他的还需要注意的是,<header/> 或者<footer/>之类的标签容易被当成“系统标签”,eslint会报错,所以最好写成"cmpHeader"之类的。
将项目移动到电脑盘的另外一个位置,居然是可以的(目标位置有git)。
安居客项目的vue改写
好大的工程。
有必要将其重新做个博客记载下来。同时,测试、源码也都有必要单独开一个博客记录。
一开始让你用eslint,结果你没用,现在很后悔。写的代码❌,找都找不到哪儿错了。
eslint的注销方法:
config/index.js中找到eslint,改成“false”。(前提是建项目的时候你确实是下载了eslint )
路由(routes/index.js)
路由只需要写父组件,子组件的注册/配置写在父组件里:
routes: [
{
path: '/',
name: 'List',
component: List
}
]
组件
父组件(list.vue)
子组件的注册/配置写在父组件里:
<script>
import ListItem from "./list_item"
export default {
name:"List",
components:{ListItem}
}
</script>
子组件(list_item.vue)
<script>
export default {
name:"ListItem",
}
</script>
父组件通过自带的$attrs,传递数据给子组件
分析:从这个功能的实现来看,这个功能显得有些多余和呆板,父组件完全没有必要把数据传给子组件再通过子组件展示数据的,完全可以自己展示数据。因此,这个这个功能过于理想化、学院化。
父组件:
父组件data(){数组【】},是一个数组,这一个数组是要被传送到子组件的对象。在传递这个数组的时候,由于在父组件中使用了v-for,即把数组中的json一个一个传递过去的,因此,子组件接受的时候是个json而非数组。
<div>
<ListItem v-for="item in items" :shuju="item"/>
</div>
data () {
return{
items:[
{name:"衣服",price:"100",sales:"1000"},
{name:"鞋子",price:"50",sales:"100"},
{name:"娃娃",price:"2034",sales:"0"}
]
}
},
子组件:
在使用子组件调用父组件的数据的时候,使用了组件的生命周期知识:
https://cn.vuejs.org/v2/guide/instance.html#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%9B%BE%E7%A4%BA
组件生命周期中要注意2个方法:mounted、beforeUpdate,这两种方法是常用的方法。
在传递这个数组的时候,由于在父组件中使用了v-for,即把数组中的json一个一个传递过去的,因此,子组件接受的时候是个json而非数组。
<template>
<div>
<ul>
<h2>商品名称:{{this.data.name}}</h2>
<li>商品价格:¥{{this.data.price}}</li>
<li>商品销量:{{this.data.sales}}</li>
</ul>
</div>
</template>
data () {
return{
data:{}
}
},
mounted () {
this.data= this.$attrs.shuju;
// console.log(this.$attrs.shuju);
console.log(this.data)
}
子组件传递数据给父组件
通过函数的方式(调用父组件的方法,调用父组件的方法之后修改了父组件的数据)实现。
子组件传递数据给父组件,本质上还是通过父组件绑定数据、绑定方法、先把数据固定在h5标签上,之后子组件通过this.$attrs进行数据的更改(这里有个隐含的点:当子组件调用父组件h5标签中的方法的时候,跟父组件自我调用该标签内的方法的作用相同)。由于是调用的方法,方法只要执行,则必定会触发相应的数据,达到修改数据的目的。
要注意的有2点:
1.需要修改的数据、以及修改数据的方法,都是放在父组件之内的。
2.了解其本质,子组件传递数据给父组件,并没有出现或者创造新的方法,依然用的是父组件传递数据给子组件的this.$attrs.xxx(方法名或者数据),但是不是直接调用父组件的数据,二是调用父组件的方法,调用父组件的方法之后修改了父组件的数据。
3.可以绑定的不仅可以是数据也可以是方法。比如:add="addone",addone是methods中的方法,“:add”是addone()的别名。
父组件:
<div>
<p>已经选中{{this.count}}件</p>
<ListItem v-for="item in items" :minus="minusone" :add="addone" :shuju="item" v-bind:key="item.id" />
</div>
<script>
import ListItem from "./list_item"
export default {
name:"List",
data () {
return{
count:0,
.......................
}
},
methods:{
addone () {
this.count++;
},
minusone () {
this.count--;
}
},
...................
}
</script>
子组件:
<div>
<ul>
<input type="checkbox" v-model="a">
<h2>商品名称:{{this.data.name}}</h2>
<li>商品价格:¥{{this.data.price}}</li>
<li>商品销量:{{this.data.sales}}</li>
</ul>
</div>
<script>
export default {
name:"ListItem",
data () {
return{
a:false,
data:{}
}
},
..............
watch:{
a () {
// alert(this.a)
if(this.a){
this.$attrs.add();
}else{
this.$attrs.minus();
}
}
}
}
</script>
axios
vue版本的ajax.,与原生fetch、jquery的ajax三足鼎立。(原生的ajax封装的太失败)
axios的安装:
https://www.cnblogs.com/m1754171640/p/13705845.html
视频中使用的第一种安装,需要挂载到原型上:
import axios from 'axios' 引入axios Vue.prototype.$http = axios 挂载在原型上
第二种方法:vue-axios 和 axios 配合使用, 下载 cnpm i axios -S(第一步) +cnpm i vue-axios -S(第二步)[不建议使用npm,报错],同样在main.js文件中引入(推荐使用)
import Vue from 'vue'; import axios from 'axios'; //引入axios import VueAxios from 'vue-axios'; // 引入vue-axios Vue.use(VueAxios,axios); //使用
这篇文章还讲解到了第二个方法的使用:
https://www.cnblogs.com/sybboy/p/7249987.html
https://www.cnblogs.com/silent007/p/8603367.html
提供axios的三种写法,最后一种写法是多线程的:
// 第一种方法
axios.get('/static/caiyicai.txt')
.then(function (response) {
console.log("第一种:"+response.data);
})
.catch(function (error) {
console.log(error);
});
// 第二种方法
axios.get('/static/caiyicai.txt', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log("第2种:"+response.data);
})
.catch(function (error) {
console.log(error);
});
// 多线程版本
function getUserAccount() {
return axios.get('/static/caiyicai.txt');
}
function getUserPermissions() {
return axios.get('/static/caiyicai.txt');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// 两个请求现在都执行完成
console.log("第3种:"+acct.data)
console.log("第3(2)种:"+perms.data)
}));
尝试多次post的用法都无法得到好的效果,看来你对post、get还没有从根本上来理解,这里有个文件较好,有时间的时候记得慢慢的看看。
https://www.zhihu.com/question/28586791
通过axios直接解析传递过来的数据
这里百度到vue的2个ajax版本问题,其中vue-resource之前在vue基础知识(https://blog.csdn.net/u011483658/article/details/107920902)讲过:
vue 项目中常用的 2 个 ajax 库
1.1.vue-resource
vue 插件, 非官方库,vue1.x 使用广泛
1.2.axios
通用的 ajax 请求库, 官方推荐,vue2.x 使用广泛
-----------------------------------------------------------------
通过不同的方式传递数据的时候,axios会有不同的修改和反应:
通过文本的方式(get)传递过来的是文本,content-type : text/plain; charset=UTF-8, 测试 typeof = “string”,具体如下:
axios.get("/static/1.txt").then().catch()
通过跨域的方式将需要传递的内容写在服务器中的时候,服务器传递过来的数据会被智能解析,content-type:json,测试typeof='object',具体内容如下:
axios.get("http://localhost:8090/list").then().catch()
这里是自己写的用nodejs作为服务器跨域传递数据的时候,后台操作:
https://blog.csdn.net/u011483658/article/details/115855805 (5.2)。
通过跨域的方式传递过来的数据不再是个文本,typeof也不再是string,而是正正经经的object,具体而言,是promise对象:
因此可以借助于await - aysnc来进一步的分解这个对象,以便可以更好的运用这个对象。aysnc需要添加在方法名之前。通过这个方法解析出来的数据依旧是个json,而我需要的只是这个json中的data数据:
代码如下:
async mounted() {
// 下面比较繁琐的内容为具体的分析,可以一步到位,就像这样:
// let res_allin= (await this.axios.get("http://localhost:8090/list").data;
// 此次为繁琐的内容:
let res1= await this.axios.get("http://localhost:8090/list");
// this.axios.get("")得到的是个promise对象(没有await-async时)
console.log(res1);
console.log(res1.data);
// 让你见识见识promise内容的多而杂,json套了好几层:
console.log(res1.request.status);
// 我需要的只是data,promise被包装的太好了:
let res=res1.data;
this.items=res;
console.log(this.items);
},
要特别注意的是,axios虽然写法众多,但是不要把几种不同的写法糅合起来,比如下面的代码就是错误的:
let res1 = await this.axios
.get("http://localhost:8090/list")
.then(function(){})
.catch(function () {});
console.log(res1); //undefined
如果非要加catch,可以用try(){}catch(e){},同时为了避免res1成为局部变量需要将他提到外面来:
let res1;
try{
res1 = await this.axios
.get("http://localhost:8090/list");
}catch(e){
alert(e)
}
最后,还有一点要注意的是,跨域传递数据往往需要开2个服务器,当node服务器没有打开的时候,也会报错,详情见面错误总结第6个。
-------------------------------------------------------------------------------
axios写在组件中
axios通常不会写在主函数main.js中,而是会写在组件当中.【写在main.js中的axios前面不需要添加this】
为了避免在每个组件都写如下一段的废话,需要在main.js中的vue对象注册axios,与此同时,组件中的axios需要添加this,变成this.axios:
这个是main.js中需要注册的:
import axios from 'axios' //引入axios
import VueAxios from 'vue-axios'; // 引入vue-axios
Vue.use(VueAxios,axios); //使用
new Vue({
.............
axios,
..........
})
这个是组件(axios之前添加this,this.axios,如果是写在main.js中,写成this.axios好像会报错。):
export default {
data() {
return {
count: 0,
items: [],
};
},
mounted() {
this.axios
.get("http://localhost:8090/list")
.then((response) => {
console.log(typeof response.data);
console.log(response.data);
this.items = response.data;
})
.catch(function (error) {
console.log(error);
});
},
components: { cmpHeader, cmpFooter },
};
原生js新增的一个类ajax传递数据的方法:fetch
fetch是原生的js自带的一个方法,他和axios、ajax一样,都可以用来传递数据。 原生js添加这个方法,主要是原来的ajax太麻烦了。
该方法需要用到2次的await。要注意的是,第二次await的时候,await需要与方法json()同时出现,当单个出现比如.json的时候依旧只是个promise,且await+json之后解析出来的就已经是data本体,不需要.data:
async beforeMount () {
let res=fetch("http://localhost:8090/list");
console.log(res+":before"); //promise
let res1=await res;
console.log(res1+":after"); //reponse
console.log(res1);
let res2= await res1.json(); //同时使用await、json()
console.log(res2);
},
也可以一步到位:
let data1=await (await(fetch("http://localhost:8090/list"))).json()
// console.log(data1)
vuex
数据状态的管理,状态的统一管理。
vuex的几个变量:
1.state
状态,全局唯一,不能直接修改
2.getter
获取状态,函数,缓存?
3.mutation
修改状态的操作的“集合”。记录修改状态的全过程:谁在什么时候修改了什么状态,便于追溯。他本身又🈶️各种的状态。
4.action
提交mutation。mutation-action是一组冗余操作。官方解释mutation和action:mutation是同步的,action是异步的。
------
安装:
npm i vuex -S
预备阶段:
import Vuex from "vuex";
Vue.use(Vuex);
标准格式:
const store = new Vuex.Store({
strict: true, //严格模式:规定只能通过mutation修改
state:{
count:0
},
mutations:{
// 所有的mutation都具备2个参数:state、arguments(提交动作时候的参数)
// 只有mutations中的state是可以更改的,
// arguments是提交动作时候的参数,将“提交动作的幅度”放大、缩小
addCount(state,arg){
// arg=arg||1;
this.state.count=this.state.count+arg
},
minusCount(state,arg){
// arg=arg||1;
this.state.count-=arg
}
},
actions:{
// 所有的action都有两个参数:store对象(本质是json)、arg
// 可以与mutation中的方法重名,也可以不重名
addCount(store,arg){
console.log("vuex_actions_add:");
console.log(store);
// this.$store.mutations.addCount(state,arg);
store.commit("addCount",arg)
},
minusCount(store,arg){
store.commit("minusCount",arg)
}
// minusCount ({commit},arg) {
// commit("minusCount",arg)
// }
},
getters:{
}
});
同时,不要忘记了再在vue对象中添加(注册):
new Vue({
...........
store,
................
})
使用阶段(组件中的使用):
methods:{
add(){
// this.$store.actions.addCount(store)//错误写法
this.$store.dispatch('addCount',5)
},
minus(){
this.$store.dispatch('minusCount',2)
}
},
程序在执行时候的路径:
组件中寻找"dispatch"关键词===>跳转到vuex中的actions,在actions中寻找相应的方法比如“addCount”===>在actions的对应的方法中,寻找寻找关键词“commit”===>通过commit中调用的方法,确定mutations中的方法===>通过mutation修改数据
注意点
1.关于mutation:
所有的mutation都具备2个参数:state、arguments(提交动作时候的参数)。只有mutations中的state是可以更改的。 arguments是提交动作时候的参数,将“提交动作的幅度”放大、缩小。
2. 所有的action都有两个参数:store对象(本质是json)、arg,可以与mutation中的方法重名,也可以不重名。
3.actions中的store对象
addCount(store,arg){
console.log("vuex_actions_add:");
console.log(store);
// this.$store.mutations.addCount(state,arg);
}
4.actions中的不同的2种写法:
minusCount(store,arg){
store.commit("minusCount",arg)
}
minusCount ({commit},arg) {
commit("minusCount",arg)
}
5.Vue.config.productionTip = false的意思:去除掉生产版本条件下的警告消息,减少体积。
-----------------------------------------------------------------------------------------
将store独立出去
store一开始是写在main.js中的,考虑到store在实际操作中会比较大,应该将其独立出去。
(将其独立出去的)目标文件位置:src/store/index.js 。依旧将其放在src文件夹下,但是独立成文件,放在src文件下的store文件夹中。
store:
需要注意的是:export default 直接跟vuex.Stroe()对象,不需要加{}
import Vue from "vue"
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({})
main.js
main.js也需要更改,主要是需要引入文件,下面的两种写法都是可以的:
import store from "@/store/index.js";
// import store from "./store" //不需要具体到某个文件,具体到某个文件夹即可
-------------
另:
1.shake tree 可以大大减少引入的打包的量:
https://blog.csdn.net/pansuyong/article/details/96132611
2.mapState将分散写的state合起来。
vuex 、axios(fetch)的组合使用
vuex、axios组合使用需要达到的效果(目的):
将分散在各个组件中的axios(fetch)操作进行统一的管理,通通都写在vuex中。
步骤
1.组件(原来的fetch/axios改成dispatch,“开关”):
async mounted() {
// this.items = await (await fetch("http://localhost:8090/list")).json();
this.$store.dispatch("loadArr")
},
computed:{
arr(){
return this.$store.state.arr;
}
},
2.vuex--actions(fetch/axios写在了这里,便于管理):
actions: {
async loadArr(store,arg){
let arr=await (await fetch("http://localhost:8082/list")).json()
console.log(arr);
store.commit("setArr",arr)
}
},
3.vuex-mutations(拿到数据后使用数据,数据是actions-loadArr拿到的,通过commit("setArr",参数)中的参数传递数据):
mutations:{
setArr(state,arg){
state.arr=arg;
}
}
关于vuex-mutations中代码片段“state.arr=arg;”,在vue2高级版本(2.9.6版本)中是可以这么写的,不会出现数据为空找不到的情况。但是在视频中(vue2低级版本),则必须写在computed中。
要注意的是,数据必须得写在computed中,而不可写在data()中
computed:{
arr(){
return this.$store.state.arr;
}
},
(原因)在vue2的低版本中,程序是这么执行的:执行到“<p >{{this.$store.state.arr}}</p>”后,发现this.$store.state.arr=【】,这个时候虽然还没有挂载到页面上,但是已经存在于内存中了。当这个组件开始挂载的时候,执行dipatch-->actions、commit--->mutations(修改)序列,即使存在“state.arr=arg;”的关系,arg变了也只是arg变了,但是计算机并没有这个能力自动掉转过来重新将已经存在于内存中的“this.$store.state.arr=【】”变化过来。
步骤的解释:
所有的连接都交给了vuex-actions来处理,而在组件中不再直接的添加连接(请求),直接的连接(/请求)变成了一个“触发开关”(dispatch),通过这个开关执行vue-actions-fetch/axios连接,在执行vuex-actions的时候顺带触发了store.commit,从而进一步的触发mutations,达到修改数据、组件展示数据的目的。这个过程跟上文提及到的vuex的“程序在执行时候的路径”是完全一致的:
组件(dispatch). --->vuex (action) ,"commit"---->actions 修改数据---->修改后的数据在插件中的展示
----------------
getters
视频中关于这段内容胡说八道的地方太多,不建议看。建议下面的博客作为参考:
https://blog.csdn.net/qq_21980517/article/details/103361938
getters下的方法都有2个参数:state、getters。
getters的作用主要是修改、过滤state中的数据。修改较为简单,主要是过滤。过滤的时候用到了方法.filter(=>).
store-getters(尤其注意arrLength中getters的用法):
getters: {
square(state,getters){
// return state.a*state.a
return state.arr.filter(res=>res>1)
},
arrLength(state,getters){
return getters.square.length;
}
}
组件中显示:
{{this.$store.getters.arrLength}}
<p style="fontsize: 20px">getters.arr:{{this.$store.getters.square}}</p>
再次强调,当组件用到vuex中的state、getters中的数据的时候,一定得用computed取数据,而不能直接在data(){return{}}取数据!当然也可以直接的调用this.$store.state/getters(因为mounted的时候调用了dispatch->commit->mutation)。
组件中调用vuex中的数据不能显示又不能报错的时候第一时间要想到将数据写在computed中!
常见错误以及应对方法:
1.this、this、this,总是忘记添加:
2.但凡出现“cannot resolve 组件xx”的问题,首先应该考虑的是组件import路径是否正确:
同一文件夹同等级的文件,不可用这种输入:【正常情况下,这个是正确的,这里报错】
要用这种:【与正常的写法还是很大的。】
3. v-bind:key="item.id"
首先,编辑器出现下滑线,提示有错误,但是不知道错误在哪儿:
接着,终端报错误:
浏览器也报错:
无论是浏览器,还是编辑器提示错误,都指向了一个网址:
https://vuejs.org/v2/guide/list.html#Maintaining-State。
打开发现原来没有添加 v-bind:key="item.id" ,在v-for中添加之后,问题解决:编辑器没有出现下滑红线、终端没有再报错、浏览器之前的一连串报错也不见了。
4
一开始以为是路径没写对,试着多次改写axios的路径发现错误依旧存在。后来想起来npm install axios下载的时候不是太顺利,怀疑是不是下载或者下载源的问题?从新使用cnpm i axios -S下载后,问题得到好的解决!
5.then()中不能使用this(在 then的内部不能使用Vue的实例化的this, 因为在内部 this 没有被绑定。),报错的类型类似于普通的this错误:
解决办法是用箭头函数代替或者在then的外面进行替换:
.then((response) => {
console.log(typeof response.data);
console.log(response.data);
this.items = response.data;
})
参考文献:
https://blog.csdn.net/weixin_43606809/article/details/101037830
6.跨域传递数据,需要开2个服务器,其中一个服务器是用node写的,如果node服务器没有打开,只打开了一个服务器,会报错如下图:
注意这里的表述,“error in monted hook ”“error:network error”。“error in monted hook"可以确定在monted hook中发生了错误,“error:network error”提醒你检查通过跨域的方法传递过来的数据有问题。如一开始所述,两个服务器都应该打开:
7. 组件在调用vuex的时候,使用的是dispatch,而不是直接调用。
错误❌写法:
add(){
this.$store.actions.addCount(store)//错误写法
// this.$store.dispatch('addCount')
},
报错内容:
正确写法:
methods:{
add(){
// this.$store.actions.addCount(store)//错误写法
this.$store.dispatch('addCount')
},
minus(){}
},