zoukankan      html  css  js  c++  java
  • Vue3.0实战项目

    vue3.0从零开始到实战项目

    vue3.0中文官网连接:https://v3.cn.vuejs.org/

    一、安装node.js

    1. 官方文档:https://nodejs.org/en/

    安装版本必须大于12.0,建议直接安装最新稳定版本

    2. 安装 node.js 的同时,一般会自动安装 npm 的,所以不用单独安装 npm 了(如有需要,也可以命令行安装:npm install -g)

    3. 查看版本命令行:node -v 、 npm -v

     

    二、全局安装vue脚手架

    npm install @vue-cli -g

    官方文档:https://cli.vuejs.org/zh/guide/

    如果你安装的是旧版的vue-cli,需要提前卸载之后再重新安装 @vue-cli,卸载:

    npm uninstall vue-cli -g

    三、初始化项目

    版本查看:vue -V

    版本升级:npm update -g @vue-cli

    创建项目:vue create projectname

    注意:项目名称不能大写有字母

    进入项目目录:cd projecname

    运行项目:npm run serve

    项目目录结构:

    1. node_modules 依赖包,如果删除了,可以使用命令:npm install 安装

    2. public 存放整个项目默认的 HTML 模板

    3. src 源代码目录(入口文件:main.js)

    4. .browserslistrc 

    5. .editorconfig 编辑器的默认配置

    6. .eslintrc.js 

    7. .gitignore 

    8. babel.config.js 配置 babel 

    9. package.json 

    10. package-lock.json 

    11. README.md

    以下是手动选择依赖初始化:

     

     

     

    运行项目:

    cd projectname

    npm run serve

     

     

    四、vue 3.0 + vite 篇

    Vite 是一个 web 开发构建工具,由于其原生 ES 模块导入方法,它允许快速提供代码。

    通过在终端中运行以下命令,可以使用 Vite 快速构建 Vue 项目。

     

    $ npm init vite-app <project-name>

    $ cd <project-name>

    $ npm install

    $ npm run dev

     

    安装项目所需插件,比如:

    安装项目生产依赖:npm vie vue-router@next vuex@next element-plus axios -D

    安装项目开发依赖:npm i sass -S

    element-plus文档:https://element-plus.gitee.io/#/zh-CN

    安装路由 Router

    # 查看 vue-router 版本

    $ npm vie vue-router versions

    # 指定版本下载

    $ npm i -D vue-router@4.0.0-beta.11

     

     

     

     

     

     以下是vue3.0的知识点:

    Object.defineProperty => Proxy

    重构了虚拟DOM

    OptionApi => Composition API

    setup 是什么?

    setup 实际上是一个组件的入口,它运行在组件被实例化的时候,props 属性被定义之后,实际上等价于 vue2.x 版本的 beforeCreate 和 Created 这两个生命周期。

    setup 接受两个参数,第一个是 props ,第二个是 context

    setup(props, ctx) {
        console.log(props, ctx)
    }
    let Child = {
        template: `<div>{{title}}</div>`,
        setup(props, context) {
            console.log(props)
        }
    }
    
    let App = {
        template: `<div class="container"><Child title="test props"/></div>`,
        components: {
            Child
        }
    }
    Vue.createApp().mount(App,
    '#app')

    reactive

    const { reactive, toRefs } = Vue
    let App = {
        template: `<div class="container">count: {{count}}<button @click="handlerCountAdd"> Click ++ </button></div>`,
        setup() {
            const state = reactive({ count: 0 })
            const handlerCountAdd = () => {
                state.count++
            }
            return {
                ...toRefs(state),
                handlerCountAdd
            }
        }
    }
    Vue.createApp().mount(App, '#app')

    toRefs

    vue3 提供的 ref 让我们有机会创建单个的响应式的对象,在 setup 函数中 return 出去之后,在模板中可直接访问

    const App = {
          template: `<div class="container">{{value}}</div>`,
          setup() {
                const value = ref(1)
                return { value }
          }
    }
    Vue.createApp().mount(App, '#app')
    const App = {
          template: `<div class="container">{{state.value}}</div>`,
          setup() {
                const state = reactive({ value: 'reactive' })
                return { state }
          }
    }
    Vue.createApp().mount(App, '#app')
    const App = {
          template: `<div class="container">{{value}}</div>`,
          setup() {
                const state = reactive({ value: 'reactive' })
                return toRefs(state)
          }
    }
    Vue.createApp().mount(App, '#app')

    反转字符串 demo

    let App = {
        template: `<div class="container">value: <input v-model="value"/><br/>rvalue: {{rvalue}}</div>`,
        setup() {
            const state = reactive({
                value: '',
                rvalue: computed(() => state.value.split('').reverse().join(''))
            })
            return toRefs(state)
        }
    }
    Vue.createApp().mount(App, '#app')

    数据响应式

    在 Vue3 中实现数据响应式的方案由 Vue2 中的 Object.defineProperty 换成了 Proxy,关于数据响应式的Api上边说到了一些,还剩下 effect 和 watch 没有提及到,effect 是数据响应式中重要的一部分,watch 和 computed 都是基于 effect 的。

    let App = {
        template: `<div class="container"> count: {{count}} <button @click="handlerCountAdd"> Click ++ </button></div>`,
        setup() {
            const state = reactive({ count: 0, value: 1 })
            const handlerCountAdd = () => {
                state.count++
            }
            watch(
                () => state.count,
                val => {
                    console.log('watch', state.count)
                    console.log('watch', state.value)
                }
            )
            effect(() => {
                console.log('effect', state.count)
                console.log('effect', state.value)
            })
            return { ...toRefs(state), handlerCountAdd }
        }
    }
    Vue.createApp().mount(App, '#app')

    effect 在响应式数据变化的时候就会执行,执行次数根据响应式数据的个数来决定

    let App = {
        template: `<div class="container"><button @click="handlerCountAdd"> Click ++ </button></div>`,
        setup() {
            const r = ref(1)
            const s = ref(1)
            const t = ref(1)
            const handlerCountAdd = () => {
                r.value *= 1
                s.value *= 2
                t.value *= 3
            }
            effect(() => {
                console.log('effect', [r.value, s.value, t.value])
            })
            return { handlerCountAdd }
        }
    }
    Vue.createApp().mount(App, '#app')

    而 watch 则点击一次 ,只会触发执行一次

    let App = {
        template: `<div class="container"><button @click="handlerCountAdd"> Click ++ </button></div>`,
        setup() {
            const state = reactive({ count: 0, value: 1 })
            const r = ref(1)
            const s = ref(1)
            const t = ref(1)
            const handlerCountAdd = () => {
                r.value *= 1
                s.value *= 2
                t.value *= 3
            }
            watch([r, s, t], val => {
                console.log('watch', val)
            })
            return { handlerCountAdd }
        }
    }
    Vue.createApp().mount(App, '#app')

    生命周期

    beforeCreate => setup(替代)
    
    created => setup(替代)
    
    beforeMount => onBeforeMount
    
    mounted => onMounted
    
    beforeUpdate => onBeforeUpdate
    
    updated => onUpdated
    
    beforeDestroy => onBeforeUnmount
    
    destroyed => onUnmounted
    
    errorCaptured => onErrorCaptured

    全局配置

    Vue2.x 创建实例并且挂载 DOM 

    import Vue from "vue";
    import App from './App.vue'
    
    new Vue({
      render: (h) => h(App)
    }).$mount("#app");

    Vue3.0 新增 api ===> createApp 创建实例

    createApp 会产生一个 app 实例,该实例拥有全局的可配置上下文
    
    import { createApp } from 'vue'
    import App from './App.vue'
    
    createApp(App).mount('#app')

     

    vue 函数

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

    传了两个属性

    v-model:selectKeys = "selectKeys"
    import {reactive,toRef } from 'vue
    export default{
        setup(props,ctx){
        //默认执行一次
    
        //页面使用 state.selectKeys
            const state  = reactive({ //attr slots emit
                selectKeys:0
            })
        //1.直接使用
            return {
                selectKeys:state.selectKeys
            }
    
        //2.导出,页面上直接使用,数据响应式还带解构
            return {
                ...toRefs(state) 
            }
    
            onMounted(()=>{
    
            })
        }
    }

    监听路由变化

    import {reactive,toRef,watch } from 'vue
    import {useRoute} from 'vue-router'
    export default{
        setup(props,ctx){
            const state  = reactive({ //attr slots emit
                selectKeys:0
            })
            //1.watch监控路由变化
            watch(()=>route.path,(newValue)=>{
                state.selectKeys = [newValue]
            })
            //2.computed监控路由变化
            const selectKeys = computed(()=>{
                return [route.path]
            })
            return {
                selectKeys
            }
        }
    }

    vuex

    import {reactive,toRef,watch ,computed} from 'vue'
    import {useRoute} from 'vue-router'
    export default{
        setup(props,ctx){
            const route = userRoute()
            const store  = useStore()
            const state  = reactive({ //attr slots emit
                selectKeys:0
            })
            //1.watch监控路由变化
            watch(()=>route.path,(newValue)=>{
                state.selectKeys = [newValue]
            })
            //2.computed监控路由变化
            const selectKeys = computed(()=>{
                return [route.path]
            })
    
            //ref 把普通值变成包装后的结构,将属性变成响应式
            // ref(store.getters.allTime)
    
            return {
                selectKeys,
                allTime:ref(store.getters.allTime)
            }
        }
    }
    
    //store.js
    import {createStore} from 'vuex
    export default {
        state:{
    
        },
        getters:{
            allTime:()=>{
                return 0
            }
        },
        mutations:{
    
        },
        actions:{
    
        },
        modules:{
    
        }
    
    }

    组件通信

    import {reactive,toRef,watch ,computed} from 'vue'
    import {useRoute} from 'vue-router'
    import moment from 'moment'
    export default{
        setup(props,ctx){
            const state = reactive({
                form:{
                    date:moment(Date.now()).format('YYYY-MM-DD')
                }
            })
    
            //方法函数
            const onSubmit =()=>{
                //传给父组件
                this.$emit('handlePlan',state.form)
            }
            return {
                ...toRefs(state),
                onSubmit
            }
        }
    }
    
    //父组件
    <Child @handlePlan="handlePlan" />
    
    import {reactive,toRef,watch ,computed} from 'vue'
    import {useRoute} from 'vue-router'
    import moment from 'moment'
    export default{
        setup(props,ctx){
            const state = reactive({
                form:{
                    date:moment(Date.now()).format('YYYY-MM-DD')
                }
            })
    
            const handlePlan = (plan)=>{
                console.log(plan)
            }
    
            return {
                handlePlan
            }
        }
    }

    封装 API

    // 环境变量
    VUE_APP_URL = 'http://www.xxx.com:3000'
    import axios from 'axios
     const instance  = axios.create({
         baseURL:process.env.VUE_APP_URL,
         timeout:3000
     })
    
    instance.interceptors.request.use((config)=>{
        return config
    })
    
    instance.interceptors.response.use((res)=>{
        return res.data.data
    },err=>{
        return Promise.reject(err)
    })
    
    export function request(opts){
        return instance(opts)
    }
    //request.js
    import {request } from '../utils/axios'
    
    export function getPlanList(){
        return request({url:'/plan',method:'get'})
    }
    
    export function addPlan(data){
        return request({url:'/plan',method:'post',data})
    }
    
    export function deletePlan(){
        return request({url:'/plan',method:'delete',params:{id}})
    }
    
    
    //action_type.js
    export const SET_PLAN_LIST = 'SET_PLAN_LIST'
    export const ADD_PLAN = 'ADD_PLAN'
    export const DELETE_PLAN = 'DELETE_PLAN'
    
    //store.js
    import {createStore} from 'vuex'
    export * as types from './action_type'
    import * as api from './request'
    export default {
        state:{
    
        },
        getters:{
            allTime:()=>{
                return 0
            }
        },
        mutations:{
            [type.ADD_PLAN](state,payload){
               state.planList = [...state.planList,payload]
            },
            [type.DELETE_PLAN](state,payload){
                state.planList.filter(item=>{
                    return item._id !=payload._id
                })
            },
            [type.SET_PLAN_LIST](state,payload){
                
            },
        },
        actions:{
            //restful api根据不同方法返回不同的资源
            async [type.ADD_PLAN]({commit},payload){
                let plan = await api.addPlan(payload)
                commit(type.ADD_PLAN,plan)
            },
            async [type.DELETE_PLAN]({commit},payload){
                let plan = await api.deletePlan(payload)
                commit(type.DELETE_PLAN,plan)
            },
            async [type.SET_PLAN_LIST]({commit},payload){
                let plan = await api.getPlanList(payload)
                commit(type.SET_PLAN_LIST,plan)
            },
        },
        modules:{
    
        }
    
    }

    使用数据

    import {reactive,toRef,watch ,onMounted,onUpdated,compile,computed} from 'vue'
    import {useStore} from 'vuex'
    import moment from 'moment'
    import * as types from '@/store/action_types'
    export default{
        setup(props,ctx){
            const store = useStore()
            // const state = reactive({
            //     planList:store.state.planList //这样取的是默认值
            // })
            onMounted(()){
                store.dispatch(types.SET_PLAN_LIST)
            }
            //时间格式化方法
            const formatDate = (value)=>{
                return moment(value).format('YYYY-MM-DD')
            }
            return {
                ...toRefs(state.store),
                formatDate
            }
        }
    }

    简版 vue

    //1.创建虚拟节点,将虚拟节点转化为真实节点
    //2.组件的实现 setup
    //3.reactive api实现effect
    //4.diff算法
    //5.vite
    
    let { render} = Vue
    const state = {
        count:0
    }
        const vnode  = {
            tag:'div',
            props:{color:'red'},
            children:[
                {
                    tag:'p',
                    props:{color:'blue},
                    children:[
                        'vue@3-计数器'
                    ]
                },
                {
                    tag:'p',
                    props:{
                        onClick:()=>{
                            alert(state.count)
                        }
                    }
                    children:[
                        'vue@3-计数器'
                    ]
                }
            ]
        }
        render(vnode,app)
    
    export function render(vnode,container){
        // 渲染页面的方法叫patch
    
        //1.第一次渲染 2.dom-diff
        patch(null,vnode,container)
    }
    /**
    * n1 老的虚拟节点
    * n2 新的虚拟节点
    * container 容器
    */
    function patch(n1,n2,container){
        //组件的虚拟节点是一个对象,tag是一个对象
        //如果是组件,tag可能是个对象
        //后续diff可以执行这个方法
        if(typeof n2.tag ==='string'){
            //标签
            mountElement(n2,container)
        }else if(typeof n2.tag==='object'){
            
        }
    }
    
    function mountElement(vnode,container){
        const  { tag,children,props }  = vnode
        //虚拟节点和真实节点做映射关系
        let el = (vnode.el =  nodeOps.createElement(tag))
    
        if(Array.isArray(children)){
            mountChild(children,el)
        }else{
            nodeOps.hostSetElementText(el,children)
        }
        container.insert(el,container)
    }
    
    function mountChild(children,container){
        for(var i=0;i<children.length;i++){
            let child = children[i]
            patch(null,child,container)
        }
    }
    
    
    //节点操作方法
    exoprt const nodeOps = {
        //插入元素节点
        insert(child,parent,anchor){
            if(anchor){
                parent.insertBefore(child,anchor)
            }else{
                parent.appendChild(child)
            }
        },
        //移除节点
        remove(child){
            const parent  = child.parentNode;
            parent && parent.removeChild(child)
        },
        //创建节点
        createElement(tag){
            return document.createElement(tag)
        },
        //设置文本内容
        hostSetElementText(el,text){
            el.textContent = text
        }
    }

    未完待续。。。

  • 相关阅读:
    octotree神器 For Github and GitLab 火狐插件
    实用篇如何使用github(本地、远程)满足基本需求
    PPA(Personal Package Archives)简介、兴起、使用
    Sourse Insight使用过程中的常使用功能简介
    Sourse Insight使用教程及常见的问题解决办法
    github 遇到Permanently added the RSA host key for IP address '192.30.252.128' to the list of known hosts问题解决
    二叉查找树的C语言实现(一)
    初识内核链表
    container_of 和 offsetof 宏详解
    用双向链表实现一个栈
  • 原文地址:https://www.cnblogs.com/Happy-Lu/p/13162282.html
Copyright © 2011-2022 走看看