• vue3 新特性


    1 创建工程

       vue-cli v4.5/:webpack

       vite:Vite | 下一代的前端工具链

    vite为何启动快?

    开发环境使用ES6 Module,可以无需预先打包,而是采用实时编译的方式

    但是生产环境使用rollop,并不会快很多。

    webpack需要转es5。
    1. import { createApp } from 'vue'
    2. import App from './App.vue'
    3. const app = createApp(App)
    4. app.mount('#app')

    vue-router:Vue Router | Vue.js 的官方路由

    vuex:Vuex 是什么? | Vuex

    2 setup

    this 为 undefined,可通过 getCurrentInstance() 获取组件实例。

    1. export default {
    2. name:'App',
    3. components:{Demo},
    4. props:['msg','school'],//props如果不声明接收,值会在$attrs(vue2)
    5. emits:['hello'],//子组件向父组件传值要声明emit(vue2)
    6. //在beforeCreate之前执行一次,this是undefined
    7. setup(props,context){//context为上下文对象,包含attrs/slots/emit(同vue2)
    8. //数据
    9. let name = '张三'
    10. let age = 18
    11. //方法
    12. function sayHello(){
    13. alert(`我叫${name},我${age}岁了,你好啊!`)
    14. }
    15. //返回一个对象(常用)
    16. return {
    17. name,
    18. age,
    19. sayHello,
    20. }
    21. }
    22. }

    3 常用组合式API

    3.0 模拟vue3实现响应式

    实现原理:

    通过Proxy(代理):  拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。

    通过Reflect(反射):  对源对象的属性进行操作。

    1. //proxy增删改属性都能获取到;Object移植到Reflect:
    2. //#region
    3. const p = new Proxy(person,{
    4. //有人读取p的某个属性时调用
    5. get(target,propName){
    6. console.log(`有人读取了p身上的${propName}属性`)
    7. //return target[propName]
    8. return Reflect.get(target,propName)
    9. },
    10. //有人修改p的某个属性、或给p追加某个属性时调用
    11. set(target,propName,value){
    12. console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
    13. //target[propName] = value
    14. Reflect.set(target,propName,value)
    15. },
    16. //有人删除p的某个属性时调用
    17. deleteProperty(target,propName){
    18. console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
    19. //return delete target[propName]
    20. return Reflect.deleteProperty(target,propName)
    21. }
    22. })
    23. //#endregion

    3.1 ref和reactive

    ref函数

    ref函数用来定义一个响应式的数据。(基本数据)通过.value修改值

    基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的。

    对象类型的数据:内部求助了Vue3.0中的一个新函数—— reactive函数。

    1. <script>
    2. import {ref} from 'vue'
    3. export default {
    4. name: 'App',
    5. setup(){
    6. //数据
    7. let name = ref('张三')//基本类型
    8. let age = ref(18)
    9. let job = ref({//对象类型
    10. type:'前端工程师',
    11. salary:'30K'
    12. })
    13. //方法
    14. function changeInfo(){
    15. name.value = '李四'
    16. age.value = 48
    17. //ref函数返回一个RefImpl对象(引用实现的实例),实现响应式:__proto__上有setter和getter
    18. console.log(name,age)
    19. job.value.type = 'UI设计师'
    20. job.value.salary = '60K'
    21. //job.value是一个Proxy对象,封装在reactive函数中
    22. console.log(job.value)
    23. }
    24. //返回一个对象(常用)
    25. return {
    26. name,
    27. age,
    28. job,
    29. changeInfo
    30. }
    31. }
    32. }
    33. script>

    why .value

    vue3用了proxy实现响应式,那如果用reactive定义引用类型的话是正常的,如果用ref是一个基本类型的值,就没办法实现proxy的拦截,所以vue3中对ref定义的值进行了包装,变成一个对象实现proxy进行拦截达到响应式。

    reactive函数

    reactive函数定义一个对象类型的响应式数据(深层次的)。

    接收一个对象或数组,返回一个proxy对象。

    内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。

    1. <template>
    2. <h1>一个人的信息h1>
    3. <h2>姓名:{{person.name}}h2>
    4. <h2>年龄:{{person.age}}h2>
    5. <h3>工作种类:{{person.job.type}}h3>
    6. <h3>工作薪水:{{person.job.salary}}h3>
    7. <h3>爱好:{{person.hobby}}h3>
    8. <h3>测试的数据c:{{person.job.a.b.c}}h3>
    9. <button @click="changeInfo">修改人的信息button>
    10. template>
    11. <script>
    12. import {reactive} from 'vue'
    13. export default {
    14. name: 'App',
    15. setup(){
    16. //数据
    17. let person = reactive({
    18. name:'张三',
    19. age:18,
    20. job:{
    21. type:'前端工程师',
    22. salary:'30K',
    23. a:{
    24. b:{
    25. c:666
    26. }
    27. }
    28. },
    29. hobby:['抽烟','喝酒','烫头']
    30. })
    31. //方法
    32. function changeInfo(){
    33. person.name = '李四'
    34. person.age = 48
    35. person.job.type = 'UI设计师'//job直接就是proxy对象
    36. person.job.salary = '60K'
    37. person.job.a.b.c = 999
    38. person.hobby[0] = '学习'
    39. }
    40. //返回一个对象(常用)
    41. return {
    42. person,
    43. changeInfo
    44. }
    45. }
    46. }
    47. script>

    3.2 toRef和toRefs

    toRef:创建一个 ref 对象,返回ObjectRefImpl对象,用于将响应式对象中的某个属性单独提供给外部使用。两者保持引用关系

    toRefs:批量创建多个 ref 对象。

    1. <template>
    2. <h4>{{person}}h4>
    3. <h2>姓名:{{name}}h2>
    4. <h2>年龄:{{age}}h2>
    5. <h2>薪资:{{job.j1.salary}}Kh2>
    6. <button @click="name+='~'">修改姓名button>
    7. <button @click="age++">增长年龄button>
    8. <button @click="job.j1.salary++">涨薪button>
    9. template>
    10. <script>
    11. import {ref,reactive,toRef,toRefs} from 'vue'
    12. export default {
    13. name: 'Demo',
    14. setup(){
    15. //数据
    16. let person = reactive({
    17. name:'张三',
    18. age:18,
    19. job:{
    20. j1:{
    21. salary:20
    22. }
    23. }
    24. })
    25. // const name1 = person.name//无响应式
    26. // console.log('%%%',name1)
    27. // const name2 = toRef(person,'name')//有响应式
    28. // console.log('####',name2)
    29. const x = toRefs(person)//只交出第一层
    30. console.log('******',x)
    31. const ageRef = toRef(person, 'age')
    32. setTimeout(() => {
    33. person.age = 25
    34. }, 1500)
    35. setTimeout(() => {
    36. ageRef.value = 30 // .value 修改值
    37. }, 3000)
    38. //返回一个对象(常用)
    39. return {
    40. person,
    41. // name:toRef(person,'name'),
    42. // age:toRef(person,'age'),
    43. // salary:toRef(person.job.j1,'salary'),
    44. ...toRefs(person)
    45. }
    46. }
    47. }
    48. script>

    2d0d434f692ced3f2e97fddfee263e13.png

    why toRef

    对象数据分解

    3.3 computed

    1. <template>
    2. <h1>一个人的信息h1>
    3. 姓:<input type="text" v-model="person.firstName">
    4. <br>
    5. 名:<input type="text" v-model="person.lastName">
    6. <br>
    7. <span>全名:{{person.fullName}}span>
    8. <br>
    9. 全名:<input type="text" v-model="person.fullName">
    10. template>
    11. <script>
    12. import {reactive,computed} from 'vue'
    13. export default {
    14. name: 'Demo',
    15. setup(){
    16. //数据
    17. let person = reactive({
    18. firstName:'张',
    19. lastName:'三'
    20. })
    21. //计算属性——简写(没有考虑计算属性被修改的情况)
    22. /* person.fullName = computed(()=>{
    23. return person.firstName + '-' + person.lastName
    24. }) */
    25. //计算属性——完整写法(考虑读和写)
    26. person.fullName = computed({
    27. get(){
    28. return person.firstName + '-' + person.lastName
    29. },
    30. set(value){
    31. const nameArr = value.split('-')
    32. person.firstName = nameArr[0]
    33. person.lastName = nameArr[1]
    34. }
    35. })
    36. //返回一个对象(常用)
    37. return {
    38. person
    39. }
    40. }
    41. }
    42. script>

    age1具有响应式 

    3.4 watch和watchEffect

    watch

    监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。

    监视reactive定义的响应式数据中某个属性时:deep配置有效。

    1. <template>
    2. <h2>当前求和为:{{sum}}h2>
    3. <button @click="sum++">点我+1button>
    4. <hr>
    5. <h2>当前的信息为:{{msg}}h2>
    6. <button @click="msg+='!'">修改信息button>
    7. <hr>
    8. <h2>姓名:{{person.name}}h2>
    9. <h2>年龄:{{person.age}}h2>
    10. <h2>薪资:{{person.job.j1.salary}}Kh2>
    11. <button @click="person.name+='~'">修改姓名button>
    12. <button @click="person.age++">增长年龄button>
    13. <button @click="person.job.j1.salary++">涨薪button>
    14. template>
    15. <script>
    16. import {ref,reactive,watch} from 'vue'
    17. export default {
    18. name: 'Demo',
    19. setup(){
    20. //数据
    21. let sum = ref(0)
    22. let msg = ref('你好啊')
    23. let person = reactive({
    24. name:'张三',
    25. age:18,
    26. job:{
    27. j1:{
    28. salary:20
    29. }
    30. }
    31. })
    32. let person2 = ref({//ref定义
    33. name:'张三',
    34. age:18,
    35. job:{
    36. j1:{
    37. salary:20
    38. }
    39. }
    40. })
    41. //情况一:监视ref所定义的一个响应式数据(一个)
    42. watch(sum,(newValue,oldValue)=>{
    43. console.log('sum变了',newValue,oldValue)
    44. },{immediate:true})
    45. //情况二:监视ref所定义的多个响应式数据(多个)
    46. watch([sum,msg],(newValue,oldValue)=>{
    47. console.log('sum或msg变了',newValue,oldValue)
    48. },{immediate:true})
    49. /*
    50. 情况三:监视reactive所定义的一个响应式数据的全部属性(全部属性)
    51. 1.注意:此处无法正确的获取oldValue
    52. 2.注意:强制开启了深度监视(deep配置无效)
    53. */
    54. watch(person,(newValue,oldValue)=>{
    55. console.log('person变化了',newValue,oldValue)
    56. },{deep:false}) //此处的deep配置无效
    57. watch(person2,(newValue,oldValue)=>{
    58. console.log('person2的值变化了',newValue,oldValue)
    59. },{deep:true})
    60. //情况四:监视reactive所定义的一个响应式数据中的某个属性(某个属性)
    61. watch(()=>person.name,(newValue,oldValue)=>{
    62. console.log('person的name变化了',newValue,oldValue)
    63. })
    64. //情况五:监视reactive所定义的一个响应式数据中的某些属性(某些属性)
    65. watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
    66. console.log('person的name或age变化了',newValue,oldValue)
    67. })
    68. //特殊情况
    69. watch(()=>person.job,(newValue,oldValue)=>{
    70. console.log('person的job变化了',newValue,oldValue)
    71. },{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
    72. //返回一个对象(常用)
    73. return {
    74. sum,
    75. msg,
    76. person
    77. }
    78. }
    79. }
    80. script>

    watchEffect

    watch:既要指明监视的属性,也要指明监视的回调。

    watchEffect:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。初始化时执行一次,用于收集要监听的数据。

    1. <template>
    2. <h2>当前求和为:{{sum}}h2>
    3. <button @click="sum++">点我+1button>
    4. <hr>
    5. <h2>姓名:{{person.name}}h2>
    6. <h2>年龄:{{person.age}}h2>
    7. <h2>薪资:{{person.job.j1.salary}}Kh2>
    8. <button @click="person.name+='~'">修改姓名button>
    9. <button @click="person.age++">增长年龄button>
    10. <button @click="person.job.j1.salary++">涨薪button>
    11. template>
    12. <script>
    13. import {ref,reactive,watch,watchEffect} from 'vue'
    14. export default {
    15. name: 'Demo',
    16. setup(){
    17. //数据
    18. let sum = ref(0)
    19. let msg = ref('你好啊')
    20. let person = reactive({
    21. name:'张三',
    22. age:18,
    23. job:{
    24. j1:{
    25. salary:20
    26. }
    27. }
    28. })
    29. //监视
    30. /* watch(sum,(newValue,oldValue)=>{
    31. console.log('sum的值变化了',newValue,oldValue)
    32. },{immediate:true}) */
    33. watchEffect(()=>{
    34. const x1 = sum.value
    35. const x2 = person.job.j1.salary
    36. console.log('watchEffect所指定的回调执行了')
    37. })
    38. //返回一个对象(常用)
    39. return {
    40. sum,
    41. person
    42. }
    43. }
    44. }
    45. script>

    3.5 生命周期

    销毁阶段的两个名字改了,创建阶段的2个钩子在setup中调用。
    1. <template>
    2. <h2>当前求和为:{{sum}}h2>
    3. <button @click="sum++">点我+1button>
    4. template>
    5. <script>
    6. import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
    7. export default {
    8. name: 'Demo',
    9. setup(){
    10. // 等于 beforeCreate 和 created
    11. console.log('---setup---')
    12. //数据
    13. let sum = ref(0)
    14. //通过组合式API的形式去使用生命周期钩子
    15. onBeforeMount(()=>{
    16. console.log('---onBeforeMount---')
    17. })
    18. onMounted(()=>{
    19. console.log('---onMounted---')
    20. })
    21. onBeforeUpdate(()=>{
    22. console.log('---onBeforeUpdate---')
    23. })
    24. onUpdated(()=>{
    25. console.log('---onUpdated---')
    26. })
    27. onBeforeUnmount(()=>{
    28. console.log('---onBeforeUnmount---')
    29. })
    30. onUnmounted(()=>{
    31. console.log('---onUnmounted---')
    32. })
    33. //返回一个对象(常用)
    34. return {sum}
    35. },
    36. //通过配置项的形式使用生命周期钩子
    37. //#region
    38. beforeCreate() {
    39. console.log('---beforeCreate---')
    40. },
    41. created() {
    42. console.log('---created---')
    43. },
    44. beforeMount() {
    45. console.log('---beforeMount---')
    46. },
    47. mounted() {
    48. console.log('---mounted---')
    49. },
    50. beforeUpdate(){
    51. console.log('---beforeUpdate---')
    52. },
    53. updated() {
    54. console.log('---updated---')
    55. },
    56. beforeUnmount() {
    57. console.log('---beforeUnmount---')
    58. },
    59. unmounted() {
    60. console.log('---unmounted---')
    61. },
    62. //#endregion
    63. }
    64. script>

    3.6 provide/inject

    1. //祖组件
    2. setup(){
    3. ......
    4. let car = reactive({name:'奔驰',price:'40万'})
    5. provide('car',car)
    6. ......
    7. }
    8. //后代组件
    9. setup(props,context){
    10. ......
    11. const car = inject('car')
    12. return {car}
    13. ......
    14. }

    3.7 自定义hook函数

    本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin。

    1. import {reactive,onMounted,onBeforeUnmount} from 'vue'
    2. export default function (){
    3. //实现鼠标“打点”相关的数据
    4. let point = reactive({
    5. x:0,
    6. y:0
    7. })
    8. //实现鼠标“打点”相关的方法
    9. function savePoint(event){
    10. point.x = event.pageX
    11. point.y = event.pageY
    12. console.log(event.pageX,event.pageY)
    13. }
    14. //实现鼠标“打点”相关的生命周期钩子
    15. onMounted(()=>{
    16. window.addEventListener('click',savePoint)
    17. })
    18. onBeforeUnmount(()=>{
    19. window.removeEventListener('click',savePoint)
    20. })
    21. return point
    22. }
    23. <template>
    24. <h2>当前求和为:{{sum}}h2>
    25. <button @click="sum++">点我+1button>
    26. <hr>
    27. <h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}h2>
    28. template>
    29. <script>
    30. import {ref} from 'vue'
    31. import usePoint from '../hooks/usePoint'
    32. export default {
    33. name: 'Demo',
    34. setup(){
    35. //数据
    36. let sum = ref(0)
    37. let point = usePoint()
    38. //返回一个对象(常用)
    39. return {sum,point}
    40. }
    41. }
    42. script>

    3.8 优点 

    vue3定义数据和使用数据放在一起进行处理,更加易读和方便处理。

    4 其他组合式API

    shallowReactive 与 shallowRef

    shallowReactive:只处理对象最外层属性的响应式(浅响应式)。

    shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。

    readonly 与 shallowReadonly 

    readonly: 让一个响应式数据变为只读的(深只读)。

    shallowReadonly:让一个响应式数据变为只读的(浅只读)。

    toRaw 与 markRaw 

    toRaw:将一个由reactive生成的响应式对象转为普通对象

    markRaw:标记一个对象,使其永远不会再成为响应式对象。

    customRef 

    1. <template>
    2. <input type="text" v-model="keyWord">
    3. <h3>{{keyWord}}h3>
    4. template>
    5. <script>
    6. import {ref,customRef} from 'vue'
    7. export default {
    8. name: 'App',
    9. setup() {
    10. //自定义一个ref——名为:myRef
    11. function myRef(value,delay){
    12. let timer
    13. return customRef((track,trigger)=>{
    14. return {
    15. get(){
    16. console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`)
    17. track() //通知Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
    18. return value
    19. },
    20. set(newValue){
    21. console.log(`有人把myRef这个容器中数据改为了:${newValue}`)
    22. clearTimeout(timer)
    23. timer = setTimeout(()=>{
    24. value = newValue
    25. trigger() //通知Vue去重新解析模板
    26. },delay)
    27. },
    28. }
    29. })
    30. }
    31. // let keyWord = ref('hello') //使用Vue提供的ref
    32. let keyWord = myRef('hello',500) //使用程序员自定义的ref
    33. return {keyWord}
    34. }
    35. }
    36. script>

    响应式数据的判断

    isRef: 检查一个值是否为一个 ref 对象

    isReactive: 检查一个对象是否是由 `reactive` 创建的响应式代理

    isReadonly: 检查一个对象是否是由 `readonly` 创建的只读代理

    isProxy: 检查一个对象是否是由 `reactive` 或者 `readonly` 方法创建的代理

    5 新组件

    Fragment

    在Vue2中: 组件必须有一个根标签

    在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中

    好处: 减少标签层级, 减小内存占用

    Teleport

     `Teleport` 是一种能够将我们的组件html结构移动到指定位置的技术。

    1. <teleport to="body">
    2. <div v-if="isShow" class="mask">
    3. <div class="dialog">
    4. <h3>我是一个弹窗h3>
    5. <button @click="isShow = false">关闭弹窗button>
    6. div>
    7. div>
    8. teleport>

     Suspense

    异步渲染子组件:通过插槽控制显示

    1. <template>
    2. <div class="app">
    3. <h3>我是App组件h3>
    4. <Suspense>
    5. <template v-slot:default>
    6. <Child/>
    7. template>
    8. <template v-slot:fallback>
    9. <h3>稍等,加载中...h3>
    10. template>
    11. Suspense>
    12. div>
    13. template>
    14. <script>
    15. // import Child from './components/Child'//静态引入
    16. import {defineAsyncComponent} from 'vue'
    17. const Child = defineAsyncComponent(()=>import('./components/Child')) //异步引入
    18. export default {
    19. name:'App',
    20. components:{Child},
    21. }
    22. script>
    23. <style>
    24. .app{
    25. background-color: gray;
    26. padding: 10px;
    27. }
    28. style>

    6 其他改变

    • createAPP

    通过 按需导入的 createStore 方法来来构建 store 实例

    通过 按需导入的 createRouter 方法来构建 router 实例

    • emits属性

            子组件需要声明自定义事件;

    • 多事件处理

    • 移除.sync 

    • 异步组件

    •   移除过滤器

            可以用computed实现

    •  移除keyCode修饰符;修改过渡类名。

    7 vue3速度更快 

    1. Proxy响应式

            按需实现深度监听

         2. PatchFlag & hoistStatic & cacheHandler

            https://vue-next-template-explorer.netlify.app

            PatchFlag:编译模板时,给动态节点做标记,如TEXT/ CLASS/ PROPS。diff算法根据标记做对比。

            hoistStatic:将静态节点的定义,提升到父作用域,缓存起来;多个相邻的静态节点合并。

            cacheHandler:事件缓存。

         3.SSR优化

            静态节点直接输出,绕过了vdom

        4. tree shaking

            编译时,根据不同的情况,引入不同的API

    8 vue中使用JSX

    8.1 基本使用

    1. //.jsx文件
    2. import { defineComponent } from 'vue'
    3. export default defineComponent({//传递setup函数或组件配置
    4. props: ['a'],
    5. setup(props) {
    6. const render = () => {
    7. return <p>Child {props.a}p>
    8. }
    9. return render
    10. }
    11. })

    8.2 对比template&JSX

    JSX本质是js代码,可以使用js的任何能力

    template只能嵌入简单的js表达式,其他需要指令,如v-if

    templateJSX
    插值{{ msg }}{ msg }
    组件定义大小写大写
    属性:name="name"name={name}
    事件@click="demo(123)"onClick={()=>demo(123)}
    条件v-if

    {flagRef.value && }

    循环v-for

    {state.list.map(item =>

  • {item}
  • )}

    插槽v-slot通过函数传参实现
    1. import { defineComponent } from 'vue'
    2. import Child from './Child'
    3. export default defineComponent(() => {
    4. function render(msg) {
    5. return <p>msg: {msg} 123123p>
    6. }
    7. return () => {
    8. return <>
    9. <p>Demo - JSXp>
    10. <Child render={render}>Child>
    11. }
    12. })
    13. import { defineComponent, ref } from 'vue'
    14. export default defineComponent({
    15. props: ['render'],
    16. setup(props) {
    17. const msgRef = ref('作用域插槽 Child - JSX')
    18. return () => {
    19. return <p>{props.render(msgRef.value)}p>
    20. }
    21. }
    22. })

    script setup(v3.2)

    定义属性defineProps;定义事件defineEmits;defineExpose暴露数据给父组件

    1. <script setup>
    2. //无setup函数,无需return
    3. import { ref, reactive, toRefs, onMounted } from 'vue'
    4. //组件无需注册
    5. import Child1 from './Child1'
    6. import Child2 from './Child2'
    7. import Child3 from './Child3'
    8. //ref
    9. const countRef = ref(100)
    10. function addCount() {
    11. countRef.value++
    12. }
    13. //reactive
    14. const state = reactive({
    15. name: '双越'
    16. })
    17. const { name } = toRefs(state)
    18. //和其他script同时使用
    19. console.log( add(10, 20) )
    20. //触发自定义事件
    21. function onChange(info) {
    22. console.log('on change', info)
    23. }
    24. function onDelete(info) {
    25. console.log('on delete', info)
    26. }
    27. //获取子组件数据
    28. const child3Ref = ref(null)
    29. onMounted(() => {
    30. // 拿到 Child3 组件的一些数据
    31. console.log(child3Ref.value)
    32. console.log(child3Ref.value.a)
    33. console.log(child3Ref.value.b)
    34. })
    35. script>
    36. Child2.vue
    37. Child3.vue

    10 TS支持

    ts的主要优势在于静态类型检查和环境声明

  • 相关阅读:
    91.(leaflet之家)leaflet态势标绘-进攻方向绘制
    图文并茂,讲解TCP和UDP协议的原理以及区别
    【网络编程】Linux网络编程基础与实战第三弹——网络名词术语
    Latex 超长矩阵如何处理显示问题
    PHP 通过 Redis 解决并发请求的操作问题
    JavaScript日期库之date-fn.js
    git如何生成ssh密钥 git生成配置ssh密钥key详细步骤
    多对多的创建方式与Ajax
    nvm 常用命令
    MySQL服务复制
  • 原文地址:https://blog.csdn.net/weixin_46151381/article/details/127768937