vue项目开发(vue2与vue3对比)

在这里插入图片描述

安装拉取项目

注意:Vite 需要 Node.js 版本 >= 12.0.0 !!! node -V vite官网

npm init vite@latest
//vue
//TypeScript
//cd npm install
//npm run dev

在这里插入图片描述
安装router和vuex:

npm install vue-router@next vuex@next

vue中使用typescrip

初与typescrip的约定情愫:最近项目要用到vue3,所以打算使用typescrip语法!
安装:

npx tyarn add typescript @vue/cli-plugin-typescript -D
npx tsc --init

修改 tsconfig.json:

{
    
    
  "compilerOptions": {
    
    
    "target": "esnext",
    "useDefineForClassFields": true,
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom"],
    "baseUrl": ".",
    "paths": {
    
    
      // "@": ["src"],
      "@/*": ["src/*"],
      "@u/*": ["src/utils/*"],
      "@c/*": ["src/components/*"],
      "@images/*": ["src/assets/images/*"]
    },
    "suppressImplicitAnyIndexErrors": true,
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
  "references": [{
    
     "path": "./tsconfig.node.json" }]
}

将main.js 改为 main.ts:

vue3.0 + vite 多环境打包 代理设置

安装cross-env
cross-env 通过js在平台设置不同的环境变量的工具

npm install --save-dev cross-env

项目根目录新建 .env.development 文件

NODE_ENV = development
VITE_BUILD_ENV = 'development'
VITE_APP_BASE_API = 'http://xxx.xxx.x.xx:xxxx/api' //测试服务器域名

项目根目录新建 .env.production 文件

VITE_ENV = production
VITE_BUILD_ENV = "production"
VITE_APP_BASE_API = "http://xx.snxxx.com" //正式服务器域名

修改package.json

"dev": "cross-env vite --mode development",
"test": "cross-env vite --mode test",
"product": "cross-env vite --mode production",
"build": "vite build",
"build:test": "cross-env vite build --mode test",
"build:product": "cross-env vite build --mode production",
//使用
process.env.NODE_ENV  环境变量(package.json 中定义的变量)
axios.defaults.baseURL = import.meta.env.VITE_APP_BASE_API;
import.meta.env.VITE_APP_BASE_API  请求地址(.env 定义的接口请求地址)
 
//设置代理
import {
    
    defineConfig, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
import * as path from 'path';
 
export default ({
    
    mode}) => {
    
    
    return defineConfig({
    
    
        plugins: [vue()],
        resolve: {
    
    
            alias: {
    
    
                '@': path.resolve(__dirname, './src')//设置别名
            }
        },
        server: {
    
    
            port: 3000,//启动端口
            open: true,
            proxy: {
    
    
                // 选项写法
                '/api': loadEnv(mode, process.cwd()).VITE_APP_BASE_API //代理网址
            },
            cors: true
        }
    })
}

vite.config.ts

import {
    
     defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
//会报错Dynamic require of "resolve" is not supported
//vite的版本是3.1.0,这个版本不支持持commonJS
// const {
    
    resolve} = require("path");
import resolve from "path";
import {
    
     createStyleImportPlugin } from "vite-plugin-style-import";

export default ({
    
     mode }) =>
  defineConfig({
    
    
    base: "./",
    build: {
    
    
      terserOptions: {
    
    
        // 生产环境移除console
        compress: {
    
    
          drop_console: true,
          drop_debugger: true,
        },
      },
      rollupOptions: {
    
    
        output: {
    
    
          //静态资源分类打包
          chunkFileNames: "assets/js/[name]-[hash].js",
          entryFileNames: "assets/js/[name]-[hash].js",
          assetFileNames: "assets/static/[name]-[hash].[ext]",
          manualChunks(id) {
    
    
            //静态资源分拆打包
            if (id.includes("node_modules")) {
    
    
              return id
                .toString()
                .split("node_modules/")[1]
                .split("/")[0]
                .toString();
            }
          },
        },
      },
    },
    resolve: {
    
    
      alias: {
    
    
        // 如果报错__dirname找不到,需要安装node,执行npm install @types/node --save-dev
        "@": resolve.resolve(__dirname, "src"),
        "@c": resolve.resolve(__dirname, "src/components"),
        "@u": resolve.resolve(__dirname, "src/utils"),
        "@images": resolve.resolve(__dirname, "src/assets/images"),
        "@views": resolve.resolve(__dirname, "src/views"),
        "@store": resolve.resolve(__dirname, "src/store"),
      },
    },	
    server: {
    
    
      open: true, // 是否自动在浏览器打开
      port: 8082,
      host: "10.1.70.70",
      proxy: {
    
    
        // "/api": {
    
    
        //   target: loadEnv(mode, process.cwd()).VITE_APP_BASEURL,
        //   changeOrigin: true,
        //   ws: true,
        //   rewrite: (resolve) => resolve.replace(/^\/api/, ""),
        // },
      },
    },
    plugins: [
      vue(),
      createStyleImportPlugin({
    
    
        //按需引入vant
        libs: [
          {
    
    
            libraryName: "vant",
            esModule: true,
            resolveStyle: (name) => {
    
    
              return `/node_modules/vant/es/${
     
     name}/style`;
            },
          },
        ],
      }),
    ],
  });

配置 const path = require(“path”); 报错:
在这里插入图片描述
所以要把vite版本改低:

"vite": "^3.1.0",

vue2和vue3区别

一、双向数据绑定原理改变

  1. vue2利用es5的API Object.definepropert() 对数据进行劫持,结合发布订阅模式来实现。vue3利用es6 proxyAPI对数据进行处理。
  2. 相比vue2,用proxy API 优势:defineProperty只能监听某个属性,不能对全对象进行监听;可以省去for in 、闭包等内容来提升效率(直接绑定整个对象即可);可以监听数组,不用再去单独对数组做特异性操作,vue3可以检测到数组内部数据的变化。

二、Vue3支持碎片(Fragments)
就是说可以拥有多个跟节点,Template 支持多个根标签。vue2不支持碎片!
Vue 3 有 createApp(),而 Vue 2 的是 new Vue(),createApp(组件),new Vue({template, render})
v-model代替以前的v-model和.sync,vue3中v-model的用法

三、Composition API)
Vue2 与vue3 最大区别是vue2使用选项类型api,对比vue3合成型api。旧的选项型api在代码里分割了不同的属性:data,computed,methods等;新的合成型api能让我们使用方法来分割,相比于旧API使用属性来分组,这样代码会更加简便和整洁。

四、建立数据data
vue2是把数据放入data中,vue3就需要使用一个新的 setup() 方法,此方法在组件初始化构造得时候触发。使用一下三个步骤来简=建立反应性数据:

  1. 从vue引入reactive();使用reactive() 方法来声明数据为响应性数据;
  2. 使用setup()方法来返回我们得响应性数据,从而template可以获取这些响应性数据。

五、生命周期

vue2   --------------------------------   vue3
beforeCreate                        ->   setup()                 -> 实例创建之前
Created                             ->   setup()                 - >实例创建之后
beforeMount                         ->   onBeforeMount           -> DOM挂载前调用
mounted                             ->   onMounted               -> DOM挂载完成调用
beforeUpdate                        ->   onBeforeUpdate          -> 数据更新之前被调用
updated                             ->   onUpdated               -> 数据更新之后被调用
beforeDestroyed                     ->   onBeforeUnmount         -> 中间销毁前调用
destroyed                           ->   onUnmounted             -> 中间销毁完成调用
activated                           ->   onActivated
deactivated                         ->   onDeactivated

六、父子传参不同,setup()函数特性

  • setup()函数接收两个参数:props、context(包含attrs、slots、emit)
  • setup函数是处于生命周期beforeCreated和created俩个钩子函数之前,执行setup时,组件实例尚未被创建(在setup()内部,this不会是该活跃实例得引用,即不指向vue实例,Vue为了避免我们错误得使用,直接将setup函数中得this修改成了undefined)与模板一起使用时,需要返回一个对象因为setup函数中,props是响应式得,当传入新的prop时,它将会被更新,所以不能使用es6解构,因为它会消除prop得响应性,如需解构prop,可以通过使用setup函数中得toRefs来完成此操作。
  • 父传子,用props,子传父用事件 Emitting
    Events。在vue2中,会调用this$emit然后传入事件名和对象;在vue3中得setup()中得第二个参数content对象中就有emit,那么我们只要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了。
  • 在setup()内使用响应式数据时,需要通过 .value 获取
import {
    
     ref } from 'vue'
const count = ref(0)
console.log(count.value)
  • 从setup() 中返回得对象上得property 返回并可以在模板中被访问时,它将自动展开为内部值。不需要在模板中追加.value。

setup函数只能是同步的不能是异步的。

七、实例化

  1. Vue2中new出的实例对象,所有的东西都在这个vue对象上,这样其实⽆论你⽤到还是没⽤到,都会跑⼀遍,这样不仅提⾼了性能消耗,也⽆疑增加了⽤户加载时间。
  2. ⽽vue3.0中可以⽤ES module,imports按需引⼊,如:keep-alive内置组件、v-model指令,等等,不仅我们开发起来更加的便捷,减少了内存消耗,也同时减少了⽤户加载时间,优化⽤户体验。

八、获取props
vue2在script代码块可以直接获取props,vue3通过setup指令传递

vue2:console.log(‘props’,this.xxx)
vue3:setup(props,context){
    
     console.log(‘props’,props) }

props属性名任意,假设为x;事件名必须为"update:x"

<Switch :value="y" @update:value="y=$event"/>
vue2中的写法
<Switch :value.sync="y"/>
vue3中的写法
<Switch v-model:value="y"/>

九、数据和方法的定义
Vue2使⽤的是选项类型API(Options API),Vue3使⽤的是合成型API(Composition API)

vue2:data() {
    
     return {
    
    }; }, methods:{
    
     }
vue3: 数据和⽅法都定义在setup中,并统⼀进⾏return{
    
    }

十、给父组件传值emit

vue2:this.$emit()
vue3:setup(props,context){
    
    context.emit()}

十一、watchEffect
Vue3中除了watch,还引入了副作用监听函数watchEffect,用过之后我发现它和React中的useEffect很像,只不过watchEffect不需要传入依赖项。

watchEffect它会立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

computed和watch所依赖的数据必须是响应式的。Vue3引入了watchEffect,watchEffect 相当于将 watch 的依赖源和回调函数合并,当任何你有用到的响应式依赖更新时,该回调函数便会重新执行。不同于 watch的是watchEffect的回调函数会被立即执行,即({ immediate: true })。

十二、组件通信

                                vue2   --------------------------  vue3
父传子                        -> props                           -> props
子传父                        -> $emit                           -> $emit 
子组访问父组件                 -> $parent                         -> 无
父组访问子组件                 -> $ref                            -> expose$ref
兄弟传值                       -> EventBus                        -> mitt

注意:
props中数据流是单项的,即子组件不可改变父组件传来的值
在组合式API中,如果想在子组件中用其它变量接收props的值时需要使用toRef将props中的属性转为响应式。

十三、路由
vue2写法:

<script>
export default {
    
    
  beforeRouteEnter (to, from, next) {
    
    
    // 在渲染该组件的对应路由被 confirm 前调用
    next()
  },
  beforeRouteEnter (to, from, next) {
    
    
    // 在渲染该组件的对应路由被 confirm 前调用
    next()
  },
  beforeRouteLeave ((to, from, next)=>{
    
    //离开当前的组件,触发
    next()       
  }),
  beforeRouteLeave((to, from, next)=>{
    
    //离开当前的组件,触发
    next()      
  }),
  methods:{
    
    
    toPage(){
    
    
      //路由跳转
      this.$router.push(xxx)
    }
  },
  created(){
    
    
    //获取params
    this.$route.params
    //获取query
    this.$route.query
  }
}
</script>

vue3的beforeRouteEnter作为路由守卫的示例是因为它在setup语法糖中是无法使用的;大家都知道setup中组件实例已经创建,是能够获取到组件实例的。而beforeRouteEnter是再进入路由前触发的,此时组件还未创建,所以是无法用在setup中的;如果想在setup语法糖中使用则需要再写一个script 如下:

<script>
export default {
    
    
  beforeRouteEnter(to, from, next) {
    
    
    // 在渲染该组件的对应路由被 confirm 前调用
    next()
  },
};
</script>

vue3路由写法:

<script>
import {
    
     defineComponent } from 'vue'
import {
    
     useRoute, useRouter } from 'vue-router'
export default defineComponent({
    
    
  beforeRouteEnter (to, from, next) {
    
    
    // 在渲染该组件的对应路由被 confirm 前调用
    next()
  },
  beforeRouteLeave ((to, from, next)=>{
    
    //离开当前的组件,触发
    next()       
  }),
  beforeRouteLeave((to, from, next)=>{
    
    //离开当前的组件,触发
    next()      
  }),
  setup() {
    
    
    const router = useRouter()
    const route = useRoute()
    const toPage = () => {
    
    
      router.push(xxx)
    }
 
    //获取params 注意是route
    route.params
    //获取query
    route.query
    return {
    
    
      toPage
    }
  },
});
</script>

十四、context.emit
新增context.emit,与this.$emit(vue3中只能在methods里使用)作用相同

import {
    
    SetupContext } from 'vue'
setup(props: Prop, context: SetupContext) {
    
    
    const toggle = () => {
    
    
      context.emit('input', !props.value)
    }
    return {
    
    toggle}
}

十五、slot具名插槽
vue2:

//子组件:
<slot name="title">

//父组件:
<template slot="title">
  <h1>哈哈哈</h1>
</template>

vue3:

```bash
//子组件:
<slot name="title">

//父组件:
<template v-slot:title>
  <h1>哈哈哈</h1>
</template>

十六、vue3中ref

//单个:
<script>
import {
    
    onMounted, ref,} from 'vue';

export default {
    
    
  setup() {
    
    
    const headline = ref(null);
    // Before the component is mounted, the value
    // of the ref is `null` which is the default
    // value we've specified above.
    onMounted(() => {
      // Logs: `Headline`
      console.log(headline.value.textContent);
    });
    return {
      // It is important to return the ref,
      // otherwise it won't work.
      headline,
    };
  },
};
</script>

<template>
  <div>
    <h1 ref="headline">
      Headline
    </h1>
    <p>Lorem ipsum ...</p>
  </div>
</template>
//v-for里的ref
<template>
  // el当前元素,divs是存储每个元素的数组
  <div v-for="(item, index) in list" :ref="el => { divs[index] = el }">
    {
    
    {
    
     item }}
  </div>
</template>

<script>
import {
    
    onMounted, ref,} from 'vue';

export default {
    
    
  setup() {
    
    
    const divs = ref([]);
    onMounted(() => {
    
    
      console.log(divs.value)
    });
    return {
    
    
      divs
    };
  },
};
</script>

十七、watchEffect

watchEffect用来代替生命周期里的onMounted和onUpdated,初始化页面的时候watchEffect里的代码会执行,当watchEffect里的数据有更新的时候同样会执行

const count = ref(0)

watchEffect(() => console.log(count.value))
// -> logs 0

setTimeout(() => {
    
    
  count.value++
  // -> logs 1
}, 100)

注意watchEffect第一次运行是在组件挂载之前,如果需要访问DOM需要将我们的watchEffect放在onMounted里

onMounted(() => {
  watchEffect(() => console.log(count.value))
})

十八、…

猜你喜欢

转载自blog.csdn.net/weixin_45788691/article/details/125727055