vue+前端速通

-目录-

前端小知识

CSS Display(显示) 与 Visibility(可见性

CSS 盒子模型

CSS 盒子模型

CSS 选择器

p 标签选择

.p 类选择

#p id选择

[tag=value] 属性选择器

组合选择器被称为 组合器(Combinators)。一共有4种组合器:

’ ‘ 后代组合器(Descendant combinator)

’>’ 直接子代组合器(Child combinator)

’~’ 一般兄弟组合器(General sibling combinator)

’+’ 紧邻兄弟组合器(Adjacent sibling combinator)

CSS position

CSS Position(定位)

一些和position配合使用的属性,在position为static时无效。

属性 说明 值
bottom 定义了定位元素下外边距边界与其包含块下边界之间的偏移,向上推 auto *length % *inherit
left 定义了定位元素左外边距边界与其包含块左边界之间的偏移。 auto *length % *inherit
right 定义了定位元素右外边距边界与其包含块右边界之间的偏移。 auto *length % *inherit
top 定义了一个定位元素的上外边距边界与其包含块上边界之间的偏移。 auto *length % *inherit

CSS display

块级元素(block)特性:

内联元素(inline)特性:

块级元素主要有:

内联元素主要有:

可变元素(根据上下文关系确定该元素是块元素还是内联元素):

CSS中块级、内联元素的应用:

利用CSS我们可以摆脱上面表格里HTML标签归类的限制,自由地在不同标签/元素上应用我们需要的属性。

主要用的CSS样式有以下三个:

我们常将所有元素加上display:inline-block样式,原本垂直的列表就可以水平显示了。

CSS 伪类

选择器 示例 示例说明
:before p:before 在每个元素之前插入内容
:after p:after 在每个元素之后插入内容
:link a:link 选择所有未访问链接
:visited a:visited 选择所有访问过的链接
:active a:active 选择正在活动链接
:hover a:hover 把鼠标放在链接上的状态

JS Promise

Promise 是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务。

const promise = new Promise((resolveCBName, rejectCBName) => {
    resolveCB(42);
}).then()
  1. Promise对象的构造函数,接收一个函数作为参数,这个函数有两个函数参数。注意:这两个函数参数仅仅传递状态和参数,并不对参数本身做任何操作(例如打印、包装)。并且这两个参数的名字随便起名,因为我们不能定义这两个函数参数,这两个参数仅仅为形参名字,这两个函数参数被Promise对象接管。

  2. 这两个函数参数作用:

    1. 传递状态

      const promise = new Promise((resolveCBName, rejectCBName) => {
          resolveCBName(); //进入then
          rejectCBName(); //之前的状态不可逆转
          console.log("init") //但是后面的代码仍然会执行
      }).then(() => {console.log("fullfilled")})
      .catch(() => {console.log("rejected")})
      //init
      //fullfilled
      
      const promise = new Promise((resolveCBName, rejectCBName) => {
          //resolveCBName();
          rejectCBName(); //进入catch
      }).then(() => {console.log("fullfilled")})
      .catch(() => {console.log("rejected")})
      //rejected
      
    2. 传递参数

      const promise = new Promise((resolveCBName, rejectCBName) => {
          resolveCBName(1);
      }).then((msg) => {console.log("fullfilled "+msg)}) //then使用一个值作为参数接收一开始的resolveCBName
          .catch((msg) => {console.log("rejected "+msg)})
      //fullfilled 1
      
      const promise = new Promise((resolveCBName, rejectCBName) => {
          //resolveCBName(1);
          rejectCBName(2);
      }).then((msg) => {console.log("fullfilled "+msg)})
      .catch((msg) => {console.log("rejected "+msg)})
      //rejected 2
      
  3. 注意,在传入的函数中,必须显式调用resolveCBName或rejectCBName,否则运行完构造函数参数后直接退出。

    const promise = new Promise((resolveCBName, rejectCBName) => {
    }).then(() => {
        console.log("fullfilled")
    }).catch(() => {
        console.log("rejected")
    })
    //打印为空
    
  4. 链式调用时,后面的使用return表示接受或者throw表示拒绝,亦或者新建Promise对象。

    const promise = new Promise((resolveCBName, rejectCBName) => {
        resolveCBName()
    }).then(() => {
        return 1//或者不写return,默认返回,都是表示接受
    }).then((msg) => {
        console.log("fullfilled " + msg)
        throw "2"
    }).catch((msg) => {
        console.log("rejected " + msg)
    })
    //fullfilled 1
    //rejected 2
    

    新建Promise对象

    const promise = new Promise((resolveCBName, rejectCBName) => {
        resolveCBName()
    }).then(() => {
        return new Promise((resolveCBName, rejectCBName) => {
            resolveCBName(1)
        })
    }).then((msg) => {
        console.log("fullfilled " + msg)
        return new Promise((resolveCBName, rejectCBName) => {
            rejectCBName(2)
        })
    }).catch((msg) => {
        console.log("rejected " + msg)
    })
    //fullfilled 1
    //rejected 2
    
  5. 实际上,catch(msg) 是 then(null, msg) 的缩略形式。二者是等同地位的。then的两个参数分别代表接受和拒绝。可以使用then而不使用catch。

    const promise = new Promise((resolveCBName, rejectCBName) => {
        resolveCBName()
    }).then(() => {
        throw "2"
    }).then(
        (msg) => {console.log("fullfilled " + msg)}, 
        (msg) => {console.log("rejected " + msg)
    })
    //rejected 2
    

    如果我们省略了 then 方法的第二个参数,那么在 Promise 对象被拒绝时会抛出一个未捕获的错误。因此,在使用 then 方法时,建议始终提供第二个参数以处理拒绝情况,或者在链式调用中使用 catch 方法来处理拒绝情况。

JS Module

js的模块机制经历了以下几个阶段:

模块模式

// 模块模式
const foo = (function () {
  const sayName = (name) => {
    console.log(`Hey, my name is ${name}`);
  };
  // 暴露公开的变量或函数
  return {
    callSayName: (name) => sayName(name),
  };
})();

//使用
foo.callSayName('name')

CommonJS

// CommonJS
// 导出一个模块
module.exports = {
  a: 1,
  b: 2,
};
// 或者
exports.a = 1;
exports.b = 2;

// 导入一个模块
const foo = require("./foo.js");

ES6 Modules

// ES6 Modules

import defaultExport from "module-name";
import { default as alias } from "module-name";

import * as name from "module-name"; //作为命名空间 使用:name.doThings();
import { export1 } from "module-name";
import { export1 as alias1 } from "module-name";
import { export1, export2 } from "module-name";
import { export1, export2 as alias2, /* … */ } from "module-name";


//同时导入default和其他,default必须放在前面
import defaultExport, { export1, /* … */ } from "module-name";  
import defaultExport, * as name from "module-name";
import "module-name"; //作为副作用引入

VUE-选项式API

父组件向传子组件传递数据

<TestPage :msg=msg @injectMsg="getChildMsg" ref="testRef"></TestPage>

props

是单向的,响应式的

在父组件vue文件template中,声明

<ChildPage v-bind:childValName=passedValue></hildPage>

在子组件vue文件script中,声明

  props: {
    //提前定义接受的变量
    msg: {
      type: String,
      default: 'nihao',
      // required: true
    }
  },

provide/inject

是单向的,响应式的

在父组件vue文件script中,声明

  provide() {
    return {
      pmsg: this.obj
    }
  }

在子组件vue文件script中,声明

  inject:['pmsg']

$refs,不推荐

父组件可以通过 $refs 获取子组件实例,然后调用子组件的方法或访问子组件的属性。

在父组件vue文件template中,声明

<child-component ref="child"></child-component>

在父组件vue文件script中,声明

this.$refs.child.childData

子组件向父组件传递数据

$emit

子组件注册事件,父组件监听,调用回调函数。

在子组件vue文件script中,声明

methods: {
    //发送父亲
    sendParent: function () {
        //定义事件,等待父监听
        this.$emit('injectMsg', this.name);
    }
},

在父组件vue文件template中,声明

<ChildPage v-on:injectMsg="getChildData"></ChildPage>

在父组件vue文件script中,声明

methods: {
    //接收孩子,收到孩子返回的信息
    getChildData: function (value) {
        this.msg = value
    }
},

使用v-on监听这个事件名称,监听到后就调用某个函数,这个函数的入参是子组件提供的。

同$refs

调用子组件的回调函数,子组件的回调函数里返回子组件数据

状态函数

状态选项 | Vue.js (vuejs.org)

名称 功能
computed 计算属性,根据现有的数据计算出衍生出的数据
watch 监听数据变化,执行对应的回调函数
$watch 与 watch 类似,但是是实例方法,可以直接在组件实例上调用
$nextTick 在下次 DOM 更新周期之后执行回调函数
$refs 访问组件实例或 DOM 元素
provide/inject 父组件向子组件注入依赖
props 父组件向子组件传递数据
$emit 子组件向父组件触发事件
$on 父组件监听子组件触发的事件

以下是这些状态函数的功能说明:

  1. computed:计算属性,根据现有的数据计算出衍生出的数据,当计算所依赖的数据发生变化时会自动重新计算。
  2. watch:监听数据变化,执行对应的回调函数,可以监听单个数据的变化,也可以监听多个数据的变化。
  3. $watch:与 watch 类似,但是是实例方法,可以直接在组件实例上调用,用法与 watch 相同。
  4. $nextTick:在下次 DOM 更新周期之后执行回调函数,通常用于在修改数据后更新 DOM 后执行一些操作。
  5. $refs:访问组件实例或 DOM 元素,通过 ref 属性注册,可以在组件实例上使用 $refs 访问。
  6. provide/inject:父组件向子组件注入依赖,提供了一种依赖注入的方式,让子组件可以访问父组件的数据或方法。
  7. props:父组件向子组件传递数据,通过在子组件上绑定 props 属性来实现。
  8. $emit:子组件向父组件触发事件,可以传递数据,父组件可以在子组件上绑定自定义事件并在回调函数中接收数据。
  9. $on:父组件监听子组件触发的事件,用于接收子组件触发的自定义事件,并执行对应的回调函数。

生命周期函数

阶段 函数 功能
创建阶段 beforeCreate 实例刚被创建,数据观测(data observer)和事件机制(event/watcher)均未初始化
created 实例已经创建完成,数据观测(data observer)已经初始化,但是还未挂载(mount)
beforeMount 实例即将被挂载到页面上
mounted 实例已经被挂载到页面上,此时实例的元素可以访问到
更新阶段 beforeUpdate 组件更新之前调用,此时虚拟 DOM 并未重新渲染
updated 组件更新完成之后调用,此时虚拟 DOM 已经重新渲染
销毁阶段 beforeUnmount 实例即将被销毁,此时实例还可以访问
unmounted 实例已经被销毁,此时实例上的所有东西都不可访问

组件插空

子组件可以预留一些空位,父组件进行填入。

在子组件vue文件template中,声明

<slot name="s1" v-bind:numlist=numlist></slot>

在父组件vue文件template中,声明

<SlotContent>
    <template v-slot:s1="slotMsg">
slotContent: {{ slotMsg.numlist }}
    </template>
</SlotContent>

具名插空v-slot:name。对于数据,子组件使用v-bind绑定子组件数据,父组件使用一个自己定义的变量,再通过这个变量访问子组件数据。

vue-router路由

第一步:定义路由

使用js模块机制,可以单独定义在router.js

import {createRouter, createWebHashHistory, createWebHistory} from 'vue-router'
//预先导入组件与加载,不是"懒加载"。懒加载在只有使用时才加载,提升性能,是建议的方式。
//...
import Sidebar from "../views/Sidebar.vue";

// 定义一些路由
// 每个路由都需要映射到一个组件。
//router是一个数组格式,里面是路由对象
const routes = [
    {//重定向路由
        path: '/', 
        redirect: '/home'
    },
    {//没什么特别的路由,path指定路径,component指定组件
        path: '/home', 
        component: Home
    },
    {
#1      path: '/user/:id',//动态路由,匹配/user/123 见例1
#2      name: 'user',	//名字,用于跳转时指定名字 见例2
        component: () => import('../views/User.vue'), //懒加载
#3      props: true, //用于绑定path里的参数和组件里的props组件 见例3
        beforeEnter: (to, from, next) => { //每路守卫,根据来源和去向信息进行访问拦截
            // console.log(to, from)
            next()
        }
    },
    {
#4      path: '/user/:id*', //正则匹配,匹配/user?id=1234,query形式,见例4
        component: NotFound
    },
    {
        path: '/:path(.*)*', //正则匹配,()表示path匹配的字符串,括号后表示对整个参数的正则,使用?、+、*
        component: NotFound
    },
    {
        path: '/parent',
        alias: '/father', //别名
        component: Parent,
        children: [ //路由分组,只是路由关系,并不决定组件之间的关系,组件跳转仍需写全路径例如/parent/st1
            {
                path: 'st1', //相当于/parent/st1
                component: ST1
            },
            {
                path: 'st2',
                component: ST2
            }
        ]
    },
    {
        path: '/onshow',
#5      components: {	//components复数,表示同一路径下将要显示多个组件,见例5
            default: OnShow,
            Topbar: Topbar,
            Sidebar: Sidebar
        }
    }
]


// 创建路由实例并传递 routes 配置
const router = createRouter({
    // history函数。createWebHashHistory,createWebHistory
    history: createWebHistory(),
    routes, // 刚才定义的数组
})

//全球守卫
router.beforeEach((to, from, next) => {
    // console.log(to, from)
    next()
})

//导出
export default router;

例1 使用动态路由

父组件

<router-link to="/user/123">Go to User</router-link>
<router-view></router-view>

需要显示的组件,获取id=123

this.$route.params.id

例2 使用具名路由 + params 实现动态路由

父组件

<!--使用path后就不能再使用params,因此params必须搭配name,等同于/user/123-->
<router-link :to="{name:'user',params:{id:'123'}}">Go to User</router-link>
<router-view></router-view>

需要显示的组件,获取id=123

this.$route.params.id

例3 使用props 实现动态路由

父组件

<router-link to="/user/123">Go to User</router-link>
<router-view></router-view>

需要显示的组件,获取id=123,在script中指定props,相当于父子组件的props,可在当前组件通过this.id直接访问

export default {
    props: ['id']
}

例4 使用具名路由 + query 实现动态路由

父组件

<router-link :to="{path:'/user',query:{id:'123'}}">Go to User</router-link>
<router-view></router-view>

需要显示的组件,获取id=123

this.$route.query.id

例5 实现多组件一页面

父组件需要显示的位置,这个router-view位置是和router-link位置绑定的

<router-view></router-view>
<router-view name="Topbar"></router-view>
<router-view name="Sidebar"></router-view>

需要显示的组件无需特殊处理。

第二步,在main.js中使用使用路由

const app = createApp(App)
app.use(router)
app.mount('#app')

通过js代码控制路由(编程式导航)

// route是当前活跃的路由的对象,router是全局的路由对象
this.$router.push('/parent')
// 等同于
this.$router.push({path:'/parent'})
// 使用name才能带参数,使用path不能带params
this.$router.push({name: 'user', params: {id: 1234}})
// 带query,等同于 /parent/?id=1234, 还可以使用replace替代当前,不会在history里留下记录
this.$router.push({path: '/user', query: {id: 1234}, replace: true})
//前进正数,后退负数
this.$router.go(-1)

使用js从网络获取数据

这里创建了一个存储池。

import {reactive} from "vue";
import axios from 'axios'

const store = {
    state: reactive({msg: "helloworld"}),
    updateMsg: function () {
        this.state.msg = 'nihao'
    },
    updateFromNet1: function () {
        //原生fetch
        fetch('http://localhost:8080').then((res) => {
            return res.json()
        }).then((res) => {
                this.state.msg = res.name
            }
        )
    },
    updateFromNet2: function () {
        //三方库axios
        axios.get('http://localhost:8080').then((res) => {
            this.state.msg = res.data.name
        })
    }
}

export default store

利用reactive绑定对象,在App.vue父组件和相关子组件中可以利用provide/inject,快捷传递响应式数据。

VUEX

出现原因:大型软件数据流过于复杂,这里集中管理。

vuex具有五个概念。

组件 功能
state 存储应用的状态数据,可以被所有组件访问
getters 计算或过滤state中的数据,可以被所有组件使用
mutations 同步修改state中的数据,只能通过commit触发
actions 异步修改state中的数据,可以包含任意逻辑,只能通过dispatch触发
modules 将store分割成多个模块,每个模块有自己的state、getters、mutations和actions

定义

首先使用JS模块化定义。

//store/index.js
import {createStore} from 'vuex'
import axios from 'axios'

import moduleA from './aStore'

export default createStore({
    state: { //单一状态,基本数据
        count: 0,
        fatherName: 'Bob'
    },
    getters: {//相当于computed选项,对state的进一步计算
        getCount(state) {
            return '计数:' + state.count
        },
        //第二个参数代表自身
        getDescripCount(state, getters) {
            return 'Descrip ' + getters.getCount
        }
    },
    mutations: {//使用这个里面的函数改变数据
        //必须通过this.$store.commit('funName', payload)触发
        //首个参数必须是state,第二个是args集成到一个对象
        increment(state, payload) {
            state.count += payload.inc
        },
        changeName(state, payload) {
            state.fatherName = payload.args + ' ' + payload.name
        }
    },
    actions: {//Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。
        //必须通过this.$store.dispatch('funName', args)触发,本质上是对调用mutation的封装
        //类似mutations的state参数, context是当前store对象
        getLastNameAndChange(context, args) {
            fetch('http://localhost:8081')
                .then((res) => {
                    return res.json()
                })
                .then((res) => {
                    //必须使用mutation
                    context.commit('changeName', {name: res.name, args: args})
                })
        },
        getLastName() {
            axios.get('http://localhost:8081').then((res) => {
                console.log(res.data.name)
            })
        }
    },
    modules: {
        moduleA
    }
})

vuex模块,aStore.js

export default {
    state: {//通过$store.state.moduleA.age获取,多加了一个模块名称
        age: 14
    },
    getters: {
        //state: 当前节点的, getters: 当前节点的,
        //rootState: 根节点的, rootGetters: 根节点的
        getAllInfo(state, getters, rootState, rootGetters) {//直接通过$store.getters.getAllInfo,不需要多加
            return state.age.toString() + ' ' + rootState.count + ' ' + rootGetters.getDescripCount
        }
    },
    mutations: {//直接通过$store.commit('updateAge')
        updateAge(state) {
            state.age = 19
        }
    },
    actions: {}//直接通过$store.dispatch('funcName')
}

然后在main.js中use即可。

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

createApp(App).use(store).use(router).mount('#app')

使用

<template>
  <div class="home">
    <div>{{ $store.state.count }}</div>
    <div>{{ count }}</div>
    <div>{{ $store.state.fatherName }}</div>
    <div> {{ $store.getters.getCount }}</div>
    <div> {{ $store.getters.getDescripCount }}</div>
    <div>
       <!--使用dispatch调用action-->
      <button @click="$store.dispatch('getLastNameAndChange', 'John')">change father name</button>
    </div>
    <div>
      <button @click="$store.dispatch('getLastName')">get last name in console</button>
    </div>

    <div>{{ $store.state.moduleA.age }}</div>
    <div>{{ $store.getters.getAllInfo }}</div>
    <div>
       <!--使用commit调用mutation-->
      <button @click="$store.commit('updateAge')">update age</button>
    </div>
  </div>

</template>

<script>
// @ is an alias to /src
import {mapState} from 'vuex'

export default {
  name: 'HomeView',
  computed: {
    // 必须使用computed才能使得vuex的state和getter响应式
    otherFunc: function () {
      return ''
    },
    // //使用mapState 通过别名映射
    // ...mapState({
    //       //count1: state => state.count
    //       count1: 'count'
    //     }),
    //直接使用原来的名字
    ...mapState(['count'])
  }

}
</script>

VUE bug排除

栈溢出

网页刷新后没有东西,但是服务器重新编译后重连后出现

© 2019 - 2023 · YuYoung's Blog