vue3项目创建和新特性

一、新特性

Composition API

  • ref和reactive
  • computed和watch
  • 新的生命周期函数
  • 自定义函数 - Hooks函数

其它新增特性

  • Teleport - 瞬移组件的位置
  • Suspense - 异步加载组件的新福音
  • 全局API的修改和优化

更好的 Typescript支持

二、使用vue-cli设置vue3开发环境

2.1、环境配置

安装node:https://nodejs.org/zh-cn/

配置npm在安装全局模块时的路径和缓存cache的路径,在node.js安装目录下新建两个文件夹 node_global和node_cache,然后在cmd命令下执行如下两个命令:

npm config set prefix "D:\Program Files\nodejs\node_global"
npm config set cache "D:\Program Files\nodejs\node_cache"

然后将路径:D:\Program Files\nodejs\node_global加入到Path系统环境变量中

最后在环境变量 -> 系统变量中新建一个变量名为 “NODE_PATH”, 值为:“D:\Program Files\nodejs\node_modules”
在这里插入图片描述

设置淘宝镜像源:npm config set registry https://registry.npm.taobao.org/

安装更新命令:npm install -g @vue/cli

查看版本:vue --version

创建项目:vue create xxx

2.2、vue3基本项目创建

2.2.1、命令行模式创建项目

进入命令行模式,输入:vue create vue3-basic,回车执行命令后,使用方向键,选择 Manually select features
在这里插入图片描述
使用方向键进行切换,空格键进行选择,选择好项目需要的特性后,回车进入下一步
在这里插入图片描述
选择vue版本,选择 3.x

在这里插入图片描述
Use class-style component syntax? (y/N):是否需要使用类组件,选择N

Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? (Y/n):结合babel使用typescript,选择n

在这里插入图片描述
保存时进行Lint
在这里插入图片描述
把配置文件放到一个单独的文件里
在这里插入图片描述
Save this as a preset for future projects? (y/N):将此另存为预设值以供将来的项目使用,选择N

在这里插入图片描述
回车进行项目的创建。

在这里插入图片描述
创建完成后,进行项目根目录:cd vue3-basic,可以通过命令:npm run serve,启动项目。

2.2.2、ui界面创建项目

命令行,输入命令:vue ui,会自动启动服务,通过默认的浏览器打开创建项目的页面。

在这里插入图片描述
在这里插入图片描述
点击“创建”,再点击“在此创建新项目”按钮,进入项目创建页面。

在这里插入图片描述

后续的创建流程和命令行模式创建是类似,根据提示进行创建。

在这里插入图片描述

2.3、项目文件结构分析

上个步骤,通过vue-cli创建了项目的结构
在这里插入图片描述
各个文件和目录的功能和作用

public:存放公共文件和项目入口的html文件

src:原始文件

  • asserts:存放静态文件
  • components:存放子组件
  • App.vue:根组件
  • main.ts:入口文件
  • shims-vue.d.ts:为vue创建的定义文件

package.json:项目包管理文件

在这里插入图片描述

2.4、插件安装

推荐安装的插件

  • Chinese:简体中文安装包
  • ESLint:代码检测
    解决eslint不生效的的方法,通过在项目中新建目录.vscode,新建文件settings.json
    {
        "eslint.validate": ["typescript"]
    }
    
    在这里插入图片描述
  • Vetur:给vue文件的开发提供支持

插件安装
在这里插入图片描述

三、新特性的使用

3.1、ref的妙用

定义ref类型的数据,传入的数据都是【原始类型】,通过ref创建的变量类型数据,是响应式的。

import { ref } from 'vue'

const count = ref(0)

赋值需要通过value属性:count.value++

模板中可以直接使用:<h1>{{count}}</h1>

示例

<template>
  <div id="app">
    <img src="./assets/logo.png" alt="Vue logo">
    <!-- Ref类型的数据,展示的时候,不需要通过count.value显示数据 -->
    <h1>{{count}}</h1>
    <h1>{{double}}</h1>
    <button @click="increase">👍+1</button>
  </div>
</template>

<script lang="ts">
import { ref, computed } from 'vue'
export default {
  name: 'App',
  setup() {
    const count = ref(0)
    // 计算类型函数,computed接收一个函数,进行回调
    const double = computed(() => {
      return count.value * 2
    })
    const increase = () => {
      // count是Ref类型的数据,值存在value属性中
      count.value++
    }
    return {
      // 键和变量名称一致,可以省略key
      count,
      increase,
      double
    }
  }
};
</script>

3.2、reactive

reactive和ref非常相似,都是一个函数类型。不同的是,reactive定义数据,传入的数据类型是【对象】。

通过reactive创建的对象,对象中的每一个变量都是响应式的。但是如果需要把对象的变量数据展开,然后返回模板调用时,此时对象的变量是非响应式的,需要通过toRefs函数对对象中的每个属性进行处理,再次展开返回的单独对象属性,也是响应式的。

vue3中对象和数组中值的变更之后,变更的值依然是响应式的。

示例

<template>
  <div id="app">
    <img src="./assets/logo.png" alt="Vue logo">
    <!-- Ref类型的数据,展示的时候,不需要通过count.value显示数据 -->
    <h1>{{count}}</h1>
    <h1>{{double}}</h1>
    <button @click="increase">👍+1</button>
  </div>
</template>

<script lang="ts">
import { ref, computed, reactive, toRefs } from 'vue'
interface DataProps {
  count: number;
  double: number;
  increase: () => void;
}

export default {
  name: 'App',
  setup() {
    const data: DataProps = reactive({
      count: 0,
      increase: () => {data.count++},
      // 函数表达式只有一行且return,可以简写
      // computed会造成类型推论的循环,定义通过定义类型来解决
      double: computed(() => data.count * 2)
    })
    // 通过refData函数,把data对象中的每一个数据,都变成ref类型,可以响应式
    const refData = toRefs(data)
    return {
      // 键和变量名称一致,可以省略key
      // 通过..., 可以把对象里的变量数据展开,模板中就可以直接通过变量名称来使用
      ...refData
    }
  }
};
</script>

3.3、生命周期

  • setup() : 开始创建组件之前,在 beforeCreate 和 created 之前执行,创建的是 data 和 method
  • onBeforeMount() : 组件挂载到节点上之前执行的函数;
  • onMounted() : 组件挂载完成后执行的函数;
  • onBeforeUpdate(): 组件更新之前执行的函数;
  • onUpdated(): 组件更新完成之后执行的函数;
  • onBeforeUnmount(): 组件卸载之前执行的函数;
  • onUnmounted(): 组件卸载完成后执行的函数;
  • onActivated(): 被包含在 中的组件,会多出两个生命周期钩子函数,被激活时执行;
  • onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行;
  • onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数。

调试使用

  • onRenderTracked
  • onRenderTriggered

在这里插入图片描述

示例

import { onMounted, onUpdated, onRenderTriggered } from 'vue'

setup() {
  onMounted(() => {
    console.log('mounted')
  })
  onUpdated(() => {
    console.log('updated')
  })
  // 调试
  onRenderTriggered((event) => {
    console.log(event)
  })
}

3.4、watch:监听器

import { watch } from 'vue'


// watch 简单应用
watch(data, () => {
  document.title = 'updated ' + data.count
})
// watch 的两个参数,代表新的值和旧的值
watch(refData.count, (newValue, oldValue) => {
  console.log('old', oldValue)
  console.log('new', newValue)
  document.title = 'updated ' + data.count
})

// watch 多个值,返回的也是多个值的数组
watch([greetings, data], (newValue, oldValue) => {
  console.log('old', oldValue)
  console.log('new', newValue)
  document.title = 'updated' + greetings.value + data.count
})

// 使用 getter 的写法 watch reactive 对象中的一项
// 不能直接监听data.count,因为是非响应式的
watch([greetings, () => data.count], (newValue, oldValue) => {
  console.log('old', oldValue)
  console.log('new', newValue)
  document.title = 'updated' + greetings.value + data.count
})

3.5、鼠标追踪器

鼠标追踪器:页面点击鼠标左键,记录点击的坐标

在src目录中创建hooks目录,在hooks目录中创建useMousePosition.ts文件。

import { reactive,toRefs, onMounted, onUnmounted } from 'vue'

// 将组件内逻辑抽象成可复用的函数
function useMousePosition() {
    const position = reactive({
      x: 0,
      y: 0
    })
    // 鼠标事件的回调函数
    const updateMouse = (e: MouseEvent) => {
      position.x = e.pageX
      position.y = e.pageY
    }
    // 组件挂载时,添加事件到dom中
     onMounted(() => {
      console.log('mounted')
      document.addEventListener('click', updateMouse)
    })
    // 组件卸载时,移除dom的监听事件
    onUnmounted(() => {
      document.removeEventListener('click', updateMouse)
    })
    return toRefs(position)
}

export default useMousePosition

3.6、useURLLoader:异步获取请求数据

axios安装:npm install axios --save

import { reactive, toRefs } from 'vue'
import axios from 'axios'

function useURLLoader(url: string) {
    // 请求状态和结果数据
    const loaderData = reactive({
        result: null,
        loading: true,
        loaded: false,
        error: null
    })

    // 发送异步请求,获得data
    // 由于 axios 都有定义,所以rawData 可以轻松知道其类型
    axios.get(url).then((rawData) => {
        loaderData.loading = false
        loaderData.loaded = true
        loaderData.result = rawData.data
    }).catch(e => {
        loaderData.error = e
    })

    return toRefs(loaderData)
}

export default useURLLoader

3.7、模块化结合typescript-泛型改造

import { ref } from 'vue'
import axios from 'axios'

function useURLLoader<T>(url: string) {
    // 请求状态和结果数据
    // 声明几个ref,代表不同的状态和结果
    const result = ref<T | null>(null)
    const loading = ref(true)
    const loaded = ref(false)
    const error = ref(null)


    // 发送异步请求,获得data
    // 由于 axios 都有定义,所以rawData 可以轻松知道其类型
    axios.get(url).then((rawData) => {
        loading.value = false
        loaded.value = true
        result.value = rawData.data
    }).catch(e => {
        error.value = e
    })
    return {result, loading, loaded, error}
}

export default useURLLoader

3.8、Teleport - 瞬间移动

index.html中添加挂载点:<div id="modal"></div>

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- Teleport 挂载点 -->
    <div id="modal"></div>
  </body>
</html>

Modal.vue

<template>
<teleport to="#modal">
  <div id="center" v-if="isOpen">
    <h2><slot>this is a modal</slot></h2>
    <button @click="buttonClick">Close</button>
  </div>
</teleport>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  props: {
    isOpen: Boolean
  },
  // 定义向外发送的事件的名称
  emits: {
    'close-modal': null
  },
  setup(props, context) {
    const buttonClick = () => {
      // 向外发送事件
      context.emit('close-modal')
    }
    return {
      buttonClick
    }
  }
})
</script>
<style>
  #center {
    width: 200px;
    height: 200px;
    border: 2px solid black;
    background: white;
    position: fixed;
    left: 50%;
    top: 50%;
    margin-left: -100px;
    margin-top: -100px;
  }
</style>

3.9、Suspense异步请求组件

  • Suspense是vue3推出的一个内置的特殊组件
  • 如果使用Suspense,要返回一个promise,通过async和await实现
<template>
    <img :src="result && result.message">
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import axios from 'axios'

export default defineComponent({
    async setup() {
        const rawData = await axios.get('https://dog.ceo/api/breeds/image/random')
        return {
            result: rawData.data
        }
    }
})
</script>

使用

<Suspense>
      <template #default>
        <!-- 多个异步组件,使用div包裹 -->
        <div>
          <async-show />
          <dog-show />
        </div>
      </template>
      <!-- 异步组件未加载成功显示的内容 -->
      <template #fallback>
        <h1>Suspense Loading...</h1>
      </template>
    </Suspense>

3.10、全局API修改

  • 全局配置:app.config
  • 全局注册类API: app.component:组件注册、app.directive:全局指令
  • 行为扩展类API:app.mixin:全局混入、app.use:安装全局插件

main.ts 入口主文件中编写

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

const app = createApp(App)

app.config.isCustomElement = tag =>
tag.startswith('app-')
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)

app.config.globalProperties.customProperty = () => {}

app.mount('#app')
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值