vue3.0 beta已出,来快速实践一下吧
vue3.0 beta已出,来快速实践一下吧
本节源码立即前往
前段时间尤大在哔哩哔哩直播了vue3的预览,来简单实践一下吧
vue3的改变
- 更小更快
- 支持自定义渲染器
- 响应式修改为基于Proxy的侦测
- 深度结合typescript
- 基于treeshaking优化
- ...
创建项目😄
引入文件
克隆一个官方仓库
git clone https://github.com/vuejs/vue-next.git
打开这个项目,下载一下依赖,然后编译
npm i
npm run dev
编译文件在vue-next\packages\vue\dist,可以把它复制出来放到我们创建的项目文件夹
在vue-next\packages\vue\examples目录下有官方的示例
脚手架创建项目
首先确保你的@vue/cli为最新版本 vue -V
然后使用 vue create vue3demo创建一个vue项目,创建的时候勾选 vuex  router
然后进入项目文件夹,命令行安装插件vue add vue-next将项目升级到vue3,打开package.json可以发现都升级到了最新版本
初始化你的第一个vue3程序⭐
实例化
为了体验整个api的变化,我们先使用直接引入的方式体验效果,新建一个index.html 引入vue.global.js
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/vue.global.js"></script>
</head>
<body>
</body>
</html>
然后我们新建一个盒子用来挂载vue实例,新建一个模板当做根节点
<body>
    <div id='app'>
    </div>
    <template id="root">
        <div>
        </div>
    </template>
</body>
实例化一个vue3,vue3是按需引入,使用一个api之前需要先引入,通过createApp来实例化
<script>
    const {
        createApp
    } = Vue;
    const root = {
        template: '#root',
    }
    createApp(root).mount('#app');
</script>
setup
setup函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。
vue3将更多的方法和数据在 setup函数中定义,这就是大家都觉得像react hook 的原因,其实实现的原理是不同的,只是写法有点像;这样做的目的就是为了更好的实现逻辑复用,具体会在后面举例
与 2.x 版本生命周期相对应的组合式 API
- beforeCreate-> 使用- setup()
- created-> 使用- setup()
- beforeMount->- onBeforeMount
- mounted->- onMounted
- beforeUpdate->- onBeforeUpdate
- updated->- onUpdated
- beforeDestroy->- onBeforeUnmount
- destroyed->- onUnmounted
- errorCaptured->- onErrorCaptured
setup最后返回所有的对象和方法,注意这里的msg不是响应式对象
const root = {
    template: '#root',
    // beforeCreate  created data和methods 
    setup() {
        console.log('vue init');
        const msg = "vue init";
        return {
            msg
        };
    },
}
<template id="root">
    <div>
        {{msg}}
    </div>
</template>
ref和reactive
ref和reactive都是用来创建响应式对象的,接下来的案例都使用脚手架创建项目来进行测试
打开views/Home.vue文件,删除掉无用的东西,首先引入ref, reactive
import { ref, reactive } from "vue";
export default {
  name: "Home",
  setup() {
    // 定义一个ref响应式对象
    const count = ref(0);
    // 如果要定义多个可以使用reactive
    const state = reactive({
      size: 36,
      color: "red"
    });
    // 定义一个方法
    const increment = () => {
      count.value++;
    };
    return {
      count,
      increment,
      state
    };
  }
};
在页面中的使用和vue2是一样的
<template>
  <div>
    <div>{{count}}</div>
    <button @click="increment">increment</button>
    <div :style="{fontSize:state.size+'px',color:state.color}">reactive</div>
  </div>
</template>
toRefs
上面reactive的导出和使用,必须state.key,如果想像vue2中的data一样定义直接使用,可以使用 toRefs
引入 import { ref, reactive, toRefs } from "vue";,使用 toRefs返回展开对象,如果你不使用toRefs而直接返回对象,原对象的响应式将丢失。
return {
  count,
  increment,
  ...toRefs(state)
};
页面中使用
<div :style="{fontSize:size+'px',color:color}">reactive</div>
计算属性和监听器
我们来到about.vue中进行代码编写,首先引入需要使用的
import { reactive, watch, computed, toRefs, watchEffect } from "vue";
watchEffect 传入一个函数并立即执行,如果函数里面使用了上面定义好的响应式对象,当对象发生变化时,会再次触发这个函数
定义计算属性和监听器
export default {
  setup() {
    const state = reactive({
      num1: 1,
      //定义一个计算属性
      num2: computed(() => state.num1 * 2)
    });
    
    // 如果响应性的属性有变更,就会触发这个函数,但他是惰性的
    watchEffect(() => {
      console.log(`effect 触发了!${state.num1}`);
    });
    // 定义一个监听器
    const stop = watch(state, (val, oldVal) => {
      console.log("watch ", oldVal.num1);
    });
    //数值增加方法
    const increment = () => state.num1++;
    // 停止监听
    const stopwatch = () => stop();
    return {
      ...toRefs(state),
      stopwatch,
      increment
    };
  }
};
查看效果
dom操作
在components文件夹下新建一个DomRef.vue文件,定义一个空的ref响应数据refdemo并返回,页面中使用ref标签来绑定这个数据,然后就可以通过操作这个响应数据来操作dom
<template>
  <div>
    <div ref="refdemo">domref</div>
    <input type="range" v-model="size" />
  </div>
</template>
<script>
import { ref, watch, onMounted } from "vue";
export default {
  setup() {
    // 定义空白ref作为Template ref操作 dom
    const refdemo = ref(null);
    const size = ref(24);
    // 监听拖动 
    watch(size, (val, oldVal) => {
      refdemo.value.style.fontSize = size.value + "px";
    });
    return {
      refdemo,
      size
    };
  }
};
</script>
在home组件中使用这个组件查看效果
<DomRef></DomRef>
...
import DomRef from "../components/DomRef";
...
components: {
  HelloWorld,
  DomRef,
},
看看效果
组件通信
和vue2的通信大同小异,新建ComDemo.vue,setup函数接受一个 props 和 context 上下文
父组件>>子组件
- 父组件触发子组件事件
- 父组件得到子组件属性
- props传值
<template>
  <div>{{name}}</div>
</template>
<script>
import { inject } from "vue";
export default {
  props: {
    name: String
  },
  setup(props, context) {
    // setup中使用 prop
    console.log("props.name:" + props.name);
    // 供父组件测试
    const str = "我是子组件的str属性";
    const talk = () => {
      console.log("我被父组件触发了");
    };
    return {
      str,
      talk
    };
  }
};
</script>
来的Home.vue中使用这个组件,并给这个子组件绑定一个响应式的ref属性
<ComDemo :name="'我是父组件传值'" @talk="talk" ref="comdemo"></ComDemo>
import ComDemo from "../components/ComDemo";
...
components: {
  HelloWorld,
  DomRef,
},
 ...
setup() {
  // 定义一个ref响应式对象
  const comdemo = ref(null);
  onMounted(() => {
    //得到子组件的值
    console.log(comdemo.value.str);
    // 触发子组件事件
    comdemo.value.talk();
  });
  return {
  	...
    comdemo
  };
}
看看效果
子组件>>父组件
绑定一个方法给子组件
<ComDemo :name="'我是父组件传值'" @talk="talk" ref="comdemo"></ComDemo>
...
setup() {
const talk = e => {
  console.log(e);
};
...
return {
 ...
  talk,
  comdemo
};
...
ComDemo.vue
setup(props, context) {
  ...
  // setup中触发父组件事件
  context.emit("talk", "我是子组件 我触发你了");
  ...
  return {
    str,
    talk
  };
}
效果
inject和provide
这个和vue2使用一样
父组件定义
import { provide } from "vue";
...
provide("injectmsg", "provide talk");
后代组件使用
import { inject } from "vue";
...
const injectmsg = inject("injectmsg");
console.log("injectmsg :>> ", injectmsg);
组合式api示例❤️
前面说到 Composition API的好处,这来举一个例
放一个尤大神的demo,这是一段鼠标操作的功能代码,我们自己还需要编写一个功能来实现结合
const useMouse = () => {
  const state = reactive({
    x: 0,
    y: 0
  });
  const update = e => {
    state.x = e.pageX;
    state.y = e.pageY;
  };
  onMounted(() => {
    window.addEventListener("mousemove", update);
  });
  onUnmounted(() => {
    window.removeEventListener("mousemove", update);
  });
  return toRefs(state);
};
我们再写一个键盘方法,记录每次用户按下键盘
// 监控键盘事件
const useKeyBoard = () => {
  const status = ref(false);
  const update = () => {
    status.value = !status.value;
  };
  onMounted(() => {
    window.addEventListener("keypress", update);
  });
  onUnmounted(() => {
    window.removeEventListener("onkeydown", update);
  });
  return {
    status
  };
};
我们来到 HelloWorld.vue组件中使用这两个方法
<template>
  <div>{{status?`${x}-${y}`:`${y}-${x}`}}</div>
</template>
<script>
import { reactive, onMounted, onUnmounted, toRefs, ref } from "vue";
// 返回鼠标位置
const useMouse = () => {
  const state = reactive({
    x: 0,
    y: 0
  });
  const update = e => {
    state.x = e.pageX;
    state.y = e.pageY;
  };
  onMounted(() => {
    window.addEventListener("mousemove", update);
  });
  onUnmounted(() => {
    window.removeEventListener("mousemove", update);
  });
  return toRefs(state);
};
// 监控键盘事件
const useKeyBoard = () => {
  const status = ref(false);
  const update = () => {
    status.value = !status.value;
  };
  onMounted(() => {
    window.addEventListener("keypress", update);
  });
  onUnmounted(() => {
    window.removeEventListener("onkeydown", update);
  });
  return {
    status
  };
};
export default {
  name: "HelloWorld",
  setup() {
    return {
      ...useMouse(),
      ...useKeyBoard()
    };
  }
};
</script>
我们在页面中显示鼠标方位,当我们按下任何按键的时候,这个显示颠倒
看下效果
vuex和vuerouter💥
vuex和vuerouter在实例化方式上有了一点区别,分别使用 createStore和createRouter进行实例化
使用vuex
打开vue3demo\src\store\index.js文件
import Vuex from 'vuex'
export default Vuex.createStore({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
      console.log('当前count:',state.count);
    }
  },
  actions: {},
  modules: {}
});
新建一个 RouterAndVuex.vue组件
<template>
  <div>
    <button @click="increment">increment</button>
  </div>
</template>
<script>
import { useStore } from "vuex";
export default {
  setup() {
    const store = useStore();
    const count = store.state.count;
    console.log("vuex >>", count);
    const increment = () => {
      // mutations
      store.commit("increment");
    };
    return {
      increment,
    };
  }
};
</script>
看看效果
使用vuerouter
<template>
  <div>
    <button @click="increment">increment</button>
    <button @click="gotoAbout">about</button>
  </div>
</template>
<script>
import { useStore } from "vuex";
import { useRouter } from "vue-router";
export default {
  setup() {
    //   使用vuex
    const store = useStore();
    const count = store.state.count;
    console.log("vuex >>", count);
    const increment = () => {
      // mutations
      store.commit("increment");
    };
    // 使用vue-router
    const router = useRouter();
    const gotoAbout = () => {
      router.push("About");
    };
    return {
      increment,
      gotoAbout
    };
  }
};
</script>
看看效果
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号