• 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
        }
    }

    未完待续。。。

  • 相关阅读:
    orcal下的中国大陆地热、重力网络数据库(用户网页模块设计)
    学生管理系统毕业论文
    图像效果算法设计—静态效果
    数字温度计(单片机课程设计)
    Orcal下的中国大陆地热、重力网络数据库(数据库模块设计)
    电脑公司财务管理系统(论文+论计)
    明天开始开年会!
    今天北京下雪了!
    三天年会开完了!
    页面元素添加提示(tooltip)
  • 原文地址:https://www.cnblogs.com/Happy-Lu/p/13162282.html
Copyright © 2020-2023  润新知