当前位置: 首页 > news >正文

东莞网站优化怎样小说类网站怎么做

东莞网站优化怎样,小说类网站怎么做,做影视剧组演员垂直平台网站,随州王章欣前言 本文并非标题党#xff0c;而是实实在在的硬核文章#xff0c;如果有想要学习Vue3的网友#xff0c;可以大致的浏览一下本文#xff0c;总体来说本篇博客涵盖了Vue3中绝大部分内容#xff0c;包含常用的CompositionAPI(组合式API)、其它CompositionAPI以及一些新的特…前言 本文并非标题党而是实实在在的硬核文章如果有想要学习Vue3的网友可以大致的浏览一下本文总体来说本篇博客涵盖了Vue3中绝大部分内容包含常用的CompositionAPI(组合式API)、其它CompositionAPI以及一些新的特性Fragment、Teleport、Suspense、provide和inject。 项目搭建 既然是学习Vue3那么首先应该需要的是如何初始化项目在这里提供了两种方式供大家参考 方式一vue-cli脚手架初始化Vue3项目 官方文档创建一个项目 | Vue CLI // 查看vue/cli版本确保vue/cli版本在4.5.0以上 vue --version // 安装或者升级你的vue/cli npm install -g vue/cli // 创建 vue create vue_test // 启动 cd vue_test npm run serve方式二vite初始化Vue3项目 vite官网Vite中文网 // 创建工程 npm init vite-app project-name // 进入工程目录 cd project-name // 安装依赖 npm install // 运行 npm run dev项目目录结构分析 这里的项目目录结构分析主要是main.js文件 Vue2里面的main.js new Vue({el: #app,components: {},template: });Vue3里面的main.js import { createApp } from vue import App from ./App.vue createApp(App).mount(#app)在Vue2里面通过new Vue({})构造函数创建应用实例对象而Vue3引入的不再是Vue的构造函数引入的是一个名为createApp的工厂函数创建应用实例对象。 Vue3-devtool获取 devtool:404 Not Found Composition API setup 理解Vue3.0中一个新的配置项值为一个函数 setup是所有Composition API(组合式API)的入口 组件中所用到的数据、方法等等均要配置在setup里面 setup函数的两种返回值 若返回一个对象则对象中的属性、方法在模板中均可以直接使用若返回一个渲染函数则可以自定义渲染内容 setup的执行时机 在beforeCreate之前执行一次此时this为undefined setup的参数 props值为对象包含组件外部传递过来且组件内部声明接收了的属性 context上下文对象 attrs值为对象包含组件外部传递过来但没有在props配置中声明的属性相当于this.$attrsslots收到的插槽内容相当于this.$slotsemit分发自定义事件的函数相当于this.$emit 注意事项 尽量不要与Vue2x的配置使用 Vue2x的配置(data、methods、computed)均可以访问到setup中的属性、方法setup中不能访问Vue2x的配置(data、methods、computed)如果data里面的属性和setup里面的属性有重名则setup优先 setup不能是一个async函数因为返回值不再是return的对象而是Promise,模板看不到return对象中的属性但是后期也可以返回一个Promise实例需要Suspense和异步组件的配合 示例一setup函数的两种返回值 templateh2练习setup相关内容/h2!--h2setup返回一个对象并使用对象中的属性和方法/h2--!--p姓名{{student.name}}/p--!--p年龄{{student.age}}/p--!--button clickhello点击查看控制台信息/button--hrh2setup返回一个函数/h2 /templatescriptimport {h} from vueexport default {name: setupComponent,setup(){// 属性let student{name:张三,age:18,}// 方法function hello() {console.log(大家好我叫${student.name},今年${student.age})}return{ // 返回一个对象student,hello,}// return()h(h1,你好) // 返回一个函数}} /scriptstyle scoped/style这里需要注意的是setup里面定义的属性和方法均要return出去否则无法使用 示例二setup里面的参数和方法和配置项混合使用 templateh2setup和配置项混用/h2h2姓名{{name}}/h2h2年龄{{age}}/h2h2性别{{sex}}/h2button clicksayHellosayHelloVue3里面的方法/buttonbutton clicksayWelcomesayWelcomeVue2里面的方法/button /templatescriptexport default {name: setup01_component,data(){return{sex:男,sum:0,}},methods:{sayWelcome(){console.log(sayWelcome)},},setup(){let sum100;let name张三;let age18;function sayHello() {console.log(我叫${name}今年${age})}return{name,age,sayHello,test02,sum}}} /scriptstyle scoped/style这段代码是先实现了setup里面的属性和方法以及Vue2中配置项里面的属性和方法。接下来添加对应的混合方法 templateh2setup和配置项混用/h2h2姓名{{name}}/h2h2年龄{{age}}/h2h2性别{{sex}}/h2button clicksayHellosayHelloVue3里面的方法/buttonbutton clicksayWelcomesayWelcomeVue2里面的方法/buttonbrbrbutton clicktest01测试Vue2里面调用Vue3里面的属性和方法/buttonbrbrbutton clicktest02测试Vue3setup里面调用Vue2里面的属性和方法/buttonbrh2sum的值是{{sum}}/h2 /templatescriptexport default {name: setup01_component,data(){return{sex:男,sum:0,}},methods:{sayWelcome(){console.log(sayWelcome)},test01(){console.log(this.sex); // Vue2里面的属性(data里面的属性)// setup里面的属性console.log(this.name);console.log(this.age);// setup里面的方法this.sayHello();}},setup(){let sum100;let name张三;let age18;function sayHello() {console.log(我叫${name}今年${age})}function test02() {// setup里面的属性console.log(name);console.log(age);// data里面的属性console.log(this.sex);console.log(this.sayWelcome);}return{name,age,sayHello,test02,sum}}} /scriptstyle scoped/style这里新增了test01和test02方法分别点击控制台可以看到点击配置项里面test01方法时除了自身的sex属性有值setup里面定义的属性也有值以及方法也可以调用点击setup里面定义的test02方法时控制台只能输出setup里面定义的属性和方法而配置项里面定义的属性和方法值均为undefined。 setup里面定义的属性和方法均可以在配置项里面使用(methods、computed、watch等)而配置项里面定义的属性和方法无法在setup里面调用如果setup里面的属性和data里面的属性有重名则setup里面的属性优先 示例三setup的执行时机 setup会在beforeCreate之前执行一次 templateh2setup的执行机制/h2 /templatescriptexport default {name: setup_component03,setup(){console.log(setup)},beforeCreate(){console.log(beforeCreate)}} /scriptstyle scoped/style查看控制台的话我们看到的顺序是setupbeforeCreate setup里面context和props的使用 Vue2里面props和slot的使用 讲解setup这里面的两个参数之前先回顾一下Vue2里面的相关知识 props和自定义事件的使用attrsslot(插槽) 示例一Vue2props和自定义事件的使用 准备两个组件分别为parent.vue组件和child.vue组件 parent.vue templatediv classparent我是父组件child msg传递信息 name张三 sendParentMsggetMsg//div /template scriptimport Child from ./Child;export default {name: Parent,components: {Child},methods:{getMsg(msg){console.log(msg)}}} /script style scoped.parent{padding: 10px;background-color: red;} /stylechild.vue templatediv classchildh2我是子组件/h2p父组件传递过来的消息是:{{msg}}/pp父组件传递过来的消息是:{{name}}/pbutton clicksendMsg向父组件的传递信息/button/div /template scriptexport default {name: Child,props:{msg:{type:String,default:},name:{type:String,default:}},mounted(){console.log(this);},methods:{sendMsg(){this.$emit(sendParentMsg,通知父组件更新)}}} /script style scoped.child{padding: 10px;background-color: orange;} /style child组件对应的代码如下 templatediv classchildh2我是子组件/h2!--p父组件传递过来的消息是:{{msg}}/p--!--p父组件传递过来的消息是:{{name}}/p--p父组件传递过来的消息是:{{$attrs.msg}}/pp父组件传递过来的消息是:{{$attrs.name}}/pbutton clicksendMsg向父组件的传递信息/button/div /templatescriptexport default {name: Child,// props:{//   msg:{//     type:String,//     default://   },//   name:{//     type:String,//     default://   }// },mounted(){console.log(this);},methods:{sendMsg(){this.$emit(sendParentMsg,通知父组件更新)}}} /scriptstyle scoped.child{padding: 10px;background-color: orange;} /style 子组件通过props接收父组件传递的信息通过this.$emit()自定义事件向父组件传递信息。当使用props接收数据的时候attrs里面的数据为空如果没有使用props接收数据的话那么props里面就有值。 示例二Vue2里面slot的使用 同理准备两个组件一个Index.vue组件另一个为MySlot.vue组件 Index.vue templatediv classindexh2我是Index组件/h2!--写法一--my-slot!--插槽里面的内容--h2传入的slot参数/h2h2传入的slot参数/h2h2传入的slot参数/h2h2传入的slot参数/h2/my-slot!--写法二--my-slottemplate slotheaderh2我是header组件/h2/templatetemplate slotfooterh2我是footer附件/h2/template/my-slot/div /templatescriptimport MySlot from ./MySlot;export default {name: Index,components: {MySlot}} /scriptstyle scoped.index{padding: 10px;background: red;} /style MySlot.vue templatediv classsloth2我是MySlot组件/h2slot/slotbrslot nameheader/slotbrslot namefooter/slot/div /templatescriptexport default {name: MySlot,mounted(){console.log(this);}} /scriptstyle scoped.slot{padding: 10px;background: orange;} /style ref 作用定义一个响应式数据 语法const xxxref(initValue) 创建一个包含响应式数据的引用对象(reference对象); JS中操作数据xxx.valuexxx; 模板中读取数据不需要.value,直接 {{xxx}} 备注 接收的数据可以是基本类型也可以是对象类型基本类型的数据响应式依然是靠Object.defineProperty()的get和set完成的对象类型的数据内部求助了Vue3.0中的一个新函数-reactive函数 示例 templateh1ref/h1h2ref定义基本数据类型/h2p姓名{{name}}/pp年龄{{age}}/pp婚否{{isMarry}}/ph2ref定义对象类型/h2p爱好{{hobby}}/pp证件类型{{user.idCard}}/pp国籍{{user.nation}}/pbutton clickchangeName修改信息/button /templatescriptimport {ref} from vueexport default {name: refComponent,setup(){// 使用基本数据类型 number,string,boolean,let nameref(张三);let ageref(18);let isMarryref(false);// 使用ref定义数组let hobbyref([吃饭,睡觉,打豆豆]);// 使用ref定义对象let userref({idCard:身份证,nation:[中国,美国,英国,俄罗斯]})function changeName() {// 修改基本数据数据类型name.value李四; // ref定义的响应式数据修改数据时必需要.valueage.value20;isMarry.valuetrue;// 修改对象数据类型hobby.value[0]玩游戏;user.value.idCard港澳台居民身份证;user.value.nation[0]挪威;}return{name,age,isMarry,changeName,user,hobby}}} /scriptstyle scoped/style注意 ref定义的响应式数据修改数据时必需要.valueref定义的对象数据类型内部求助了Vue3.0中的一个新函数-reactive函数模板中使用数据时不需要.value reactive函数 作用定义一个对象类型的响应式数据(基本类型别用它用ref函数)语法const 代理对象reactive(被代理的对象)接收一个对象(或数组)返回一个代理器对象(Proxy的实例对象简称Proxy对象)reactive定义的响应式数据是深层次的内部基于ES6的Proxy实现通过代理对象的操作源对象的内部数据都是响应式的 templateh2reactive响应式数据/h2p姓名{{student.name}}/pp年龄{{student.age}}/pp爱好{{student.hobbies}}/pbutton clickchangeStuInfo改变学生信息/button /templatescriptimport {reactive} from vueexport default {name: reactiveComponent,setup(){// 数据let studentreactive({name:张三,age:19,hobbies:[吃饭,睡觉,打豆豆]});console.log(student)// 方法function changeStuInfo() {student.name李四;student.age20;student.hobbies[0]做家务 }return{student,changeStuInfo,}}} /scriptstyle scoped/stylereactive对比ref 从定义数据的角度对比 ref用来定义基本类型数据reactive用来定义对象(或数组)类型数据备注ref也可以用来定义对象或数组类型的数据它内部会自动通过reactive转为代理对象 从原理角度对比 ref通过Object.defineProperty()的get和set实现(响应式)数据劫持reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据 从使用角度 ref定义的数据操作数据需要.value,读取数据时模板中直接读取不需要.valuereactive定义的数据操作数据与读取数据均不需要.value watch和watchEffect // attr表示需要监视的属性// 情况一监视单个ref定义的响应式数据watch(attr,(newValue,oldValue){console.log(attr变化了,newValue,oldValue);})// 情况二; 监视多个ref定义的响应式数据watch([attr1,attr2,....,attrn],(newValue,oldValue){console.log(attr1或attrn变化了,newValue,oldValue);})// obj表示需要监听的对象// 情况三监视reactive定义的响应式数据// 若watch监视的是reactive定义的响应式数据则无法正确获得oldValue// 若watch监视的是reactive定义的响应式数据则强制打开开启了深度监视watch(obj,(newValue,oldValue){console.log(obj变化了,newValue,oldValue)},{immediate:true,deep:false}); // 此处deep配置不在奏效// 情况四,监视reactive定义的响应式数据中的某个属性watch(()person.job,(newValue,oldValue){console.log(person的job变化了,newValue,oldValue)},{immediate:true,deep:true})// 情况五监视reactive定义的响应式数据中的某一些属性watch([()person.name,()person.age],(newValue,oldValue){console.log(person的job变化了,newValue,oldValue)})// 特殊情况watch(()person.job,(newValue,oldValue){console.log(person的job变化了,newValue,oldValue)},{deep:false});// 此处由于是监视reactive所定义的对象中的某个属性所以deep配置有效watch 监视reactive定义的响应式数据时oldValue无法正确获取、强制开启了深度监视(deep配置失效)监视reactive定义的响应式数据中某个属性时deep配置有效 示例一wath监听ref定义的响应式数据 templateh2watch监听ref定义的响应式数据/h2h2姓名{{userName}}/h2h2年龄{{age}}/h2button clickuserName!修改姓名/buttonbutton clickage修改年龄/buttonhrh2姓名{{user.name}}/h2h2年龄{{user.age}}/h2button clickuser.name!修改姓名/buttonbutton clickuser.age修改年龄/button /templatescriptimport {ref,watch} from vue;export default {name: watch_component01,setup(){let userNameref(张三);let ageref(18);let userref({name:张三,age:21,})// watch监听ref定义的单个响应式数据watch(userName,(newValue,oldValue){console.log(userName发生了变化,新值是:${newValue},旧值是:${oldValue})});watch(age,(newValue,oldValue){console.log(age发生了变化,新值是:${newValue},旧值是:${oldValue});});// 如果需要监听多个ref定义的响应式数据的话代码如下/*** newValue和oldValue都是数组的形式其中数组的第n位表示监听位置的第n位* 例如此例子中监听属性的第一位是userName,那位newValue和oldValue对应的第一位也是* userName。* 如果有立即执行那么最开始的值为[],而不是[undefined,undefined]*/watch([userName,age],(newValue,oldValue){console.log(userName或age中的其中一个发生了变化,,newValue,oldValue)})// watch监视ref定义的响应式对象数据watch(user.value,(newValue,oldValue){console.log(person发生了变化,newValue,oldValue)})watch(user,(newValue,oldValue){console.log(person发生了变化,newValue,oldValue);},{deep:false})return{userName,age,user}}} /scriptstyle scoped/style示例二watch监听reactive定义的响应式数据 templateh1watch监听reactive定义的响应式数据/h1p姓名{{user.name}}/pp年龄{{user.age}}/pp薪水{{user.job.salary}}K/pbutton clickuser.name!改变姓名/buttonbutton clickuser.age改变年龄/buttonbutton clickuser.job.salary改变薪水/button /templatescriptimport {watch,reactive} from vueexport default {name: watch_component02,setup(){let userreactive({name:张三,age:18,job:{salary:20}});// 情况一监听reactive定义的响应式数据无法正确获取oldValue/*** 此时的newValue和oldValue都是最新的数据* 默认强制开启深度监视此时深度监视失效*/watch(user,(newValue,oldValue){console.log(newValue,oldValue);},{deep:false});// 情况二监视reactive定义的响应式数据的单个属性// watch(()user.name,(newValue,oldValue){// console.log(name发生了变化,newValue,oldValue);// });// watch(()user.age,(newValue,oldValue){// console.log(age发生了变化,newValue,oldValue);// })// 情况三监视reactive定义的响应式数据的多个属性/*** newValue和oldValue都是数组的形式其中数组的第n位表示监听位置的第n位* 例如此例子中监听属性的第一位是userName,那位newValue和oldValue对应的第一位也是* userName,*/// watch([()user.name,()user.age],(newValue,oldValue){ // 写法一// console.log(name或age中的某个属性发生了变化,newValue,oldValue);// })// watch(()[user.name,user.age],(newValue,oldValue){ // 写法二// console.log(name或者age中的某个属性发生了变化,newValue,oldValue)// })// 情况四监视reactive定义的响应式数据的对象的某个属性此时deep有效/*** 注意此时需要区别是reactive定义的对象还是reactive定义的对象里面的某个属性* 此时deep有效关闭了监视*/// watch(()user.job,(newValue,oldValue){// console.log(newValue,oldValue);// },{deep:false});return{user}}} /scriptstyle scoped/stylewatchEffect watch的套路是既要指明监视的属性也要指明监视的回调 watchEffect的套路是不用指明监视那个属性监视的回调中用到那个属性那就监视那个属性 watchEffect有点像computed 但computed注重的是计算出来的值(回调函数的返回值)所以必需要写返回值而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值 示例 templateh1watchEffect监视ref和reactive定义的响应式数据/h1h2当前求和{{sum}}/h2button clicksum点我加1/buttonhrh2当前的信息:{{msg}}/h2button clickmsg!修改信息/buttonhrh2姓名{{person.name}}/h2h2年龄{{person.age}}/h2h2薪资:{{person.job.j1.salary}}/h2button clickperson.name!修改姓名/buttonbutton clickperson.age修改年龄/buttonbutton clickperson.job.j1.salary涨薪/button /templatescriptimport {ref,reactive,watchEffect} from vue;export default {name: watch_effect_component01,setup(){let sumref(0);let msgref(你好);let personreactive({name:张三,age:18,job:{j1:{salary:100,}}});/*** 在watchEffect里面写需要监视的属性默认会执行一次* 如果是监视ref定义的响应式书则需要.value* 如果是监视reactive定义的响应式数据则直接监视*/watchEffect((){let x1sum.value;let x2person.job.j1.salary;console.log(watchEffect所指定的回调函数执行了);})return{sum,msg,person}}} /scriptstyle scoped/styleVue2响应式原理VSVue3响应式原理 在Vue2中主要是通过数据劫持来实现响应式原理的也就是基于Object.defineProperty来实现的而Vue3中是通过Proxy和Reflect来实现的(个人最低层次上的理解还望各位大佬海涵)。 那么我们来对比一下Vue3实现响应式的原理比Vue2实现响应式的原理好在哪里? Vue2响应式原理 实现原理 对象类型通过Object.defineProperty()对属性的读取修改进行拦截(数据劫持)数组类型通过重写更新数组的的一系列方法来实现拦截。对数组的变更方法进行了包裹 Object.defineProperty(data,count,{get(){}set(){} })存在问题新增属性、删除属性、界面不会自动更新直接通过下标修改数组界面不会自动更新 先看一个简单的示例 !DOCTYPE html html langen headmeta charsetUTF-8titleTitle/title /head body div idapph1学生信息/h1h4姓名{{student.name}}/h4h4年龄{{student.age}}/h4h4 v-ifstudent.sex性别:{{student.sex}}/h4h4爱好{{student.hobbies}}/h4button clickaddSex新增性别/buttonbutton clickdeleteAge删除年龄/buttonbutton clickupdateHobbies修改爱好/button /div script srchttps://cdn.bootcss.com/vue/2.5.2/vue.min.js/script scriptlet vmnew Vue({el:#app,data(){return{student:{name:张三,age:18,hobbies:[吃饭,睡觉,打豆豆]}}},methods:{addSex(){ // 新增性别this.student.sexmale;console.log(this.student);},deleteAge(){ // 删除年龄delete this.student.age;console.log(this.student);},updateHobbies(){ // 修改爱好this.student.hobbies[0]玩游戏;console.log(this.student);}}}) /script /body /html分别调用按钮对应的方法控制台可以看到新增性别属性时student里面有性别这个属性但是并没有实现响应式视图没有更新同理其它两个按钮对应的方法也是一样。 原因Vue2.0想要实现响应式数据的话必需先在data里面定义之后重新添加的数据无法实现响应式。 解决方案 新增/修改vue.$set(target,propName/index,value) {Object | Array} target{string | number} propertyName/index{any} value 删除vue.$delete(target,propName/index) {Object | Array} target{string | number} propertyName/index 此时我们修改对应的代码如下 addSex(){ // 新增性别// this.student.sexmale;// vm.$set(this.student,sex,male);this.$set(this.student,sex,male);console.log(this.student);},deleteAge(){ // 删除年龄// delete this.student.age;// this.$delete(this.student,age);vm.$delete(this.student,age);console.log(this.student);}, updateHobbies(){ // 修改爱好// this.student.hobbies[0]玩游戏;// this.$set(this.student.hobbies,0,玩游戏);// vm.$set(this.student.hobbies,0,玩游戏);/*** 或者使用数组变异的方法* push()* pop()* shift()* unshift()* splice()* sort()* reverse()*/this.student.hobbies.splice(0,1,玩游戏);console.log(this.student);}弊端 必需定义在data里面的数据才能实现响应式如果后面添加的数据想要实现响应式那么就需要调用对应的API Object.defineProperty的简单示例 let student{name:张三,age:18,}let p{}Object.defineProperty(p,name,{get(){ // 读取name时触发console.log(读取了name属性);return student.name;},set(value){ // 修改name时触发console.log(name发生了变化,视图发生了变化);student.namevalue;}});console.log(p.name);p.name李四;p.sexmale;delete p.name;Vue3响应式原理 关于Proxy和Reflect的用法这里不过多介绍如果有想要了解的推荐看MDN或者阮一峰老师的ES6 ES6 入门教程https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy 示例 let user{name:张三,age:18,}let pnew Proxy(user,{get(target,propName){console.log(读取了p里面的${propName}属性);Reflect.get(target,propName);// return target[propName];},set(target,propName,value){console.log(修改了p里面的${propName}属性);// target[propName]value;Reflect.set(target,propName,value);},deleteProperty(target,propName){console.log(删除了p里面的${propName}属性);// delete target[propName];Reflect.deleteProperty(target,propName);}});console.log(p.name);p.name李四;p.sexmale;delete p.age;查看控制台当读取name属性的时候触发get()方法新增或者修改属性的时候触发set()方法删除的时候触发deleteProperty()方法这就是Vue3.0对响应式的改进。 生命周期和钩子函数 Vue2.0生命周期和钩子函数 vue3.0生命周期和钩子函数 Vue2和Vue3生命周期对比beforeCreateNot needed*createdNot needed*beforeMountonBeforeMountmountedonMountedbeforeUpdateonBeforeUpdateupdatedonUpdatedbeforeUnmountonBeforeUnmountunmountedonUnmountedactivatedonActivateddeactivatedonDeactivated 因为 setup是围绕 beforeCreate和 created生命周期钩子运行的所以不需要显式地定义它们。换句话说在这些钩子中编写的任何代码都应该直接在 setup函数中编写。 Vue3x中可以继续使用Vue2x中的生命周期钩子但有两个被更名 beforeDestory改名为beforeUnmoutdestoryed改名为unmouted template!--Vue3x生命周期和钩子函数--h3Vue3x生命周期和钩子函数/h3h3数字{{num}}/h3button clicknum点我加加/button /templatescriptimport {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from vueexport default {name: lifeCycleComponent,setup(){let numref(0);console.log(setup);onBeforeUnmount((){console.log(onBeforeUnmount);});onMounted((){console.log(onMounted);});onBeforeUpdate((){console.log(onBeforeUpdate);});onUpdated((){console.log(onUpdated);});onBeforeUnmount((){console.log(onBeforeUnmount);})onUnmounted((){console.log(onUnmounted);});return{num,}}} /scriptstyle scoped/style自定义hook 什么是hook本质是一个函数把setup函数中使用的CompositionAPI进行了封装类似于vue2x中mixin自定义hook的优势复用代码让setup中的逻辑更清楚易懂 作为初学hook的我来说我对于hook并没有了解多少这里贴一些自己练习中的示例现阶段的我感觉不出来export 导出函数和hook的区别和优点。示例的话是鼠标点击的时候获取当前坐标 示例 templateh2自定义hook/h2h2当前x的坐标{{x}},当前y的坐标{{y}}/h2 /templatescriptimport {reactive,toRefs,onMounted,onUnmounted} from vueexport default {name: hook_component01,setup(){// 数据let pointreactive({x:0,y:0,})// 方法function getPoint(event){console.log(event)point.xevent.clientX;point.yevent.clientY;}// 生命周期钩子函数onMounted((){window.addEventListener(click,getPoint);})onUnmounted((){window.removeEventListener(click,getPoint);})return{...toRefs(point)}}} /scriptstyle scoped/style抽离单独的hook 新建目录hookhook目录下新建文件usePoint.js usePoint.js import {reactive,onMounted,onUnmounted} from vue export let getPoint(){// 数据let pointreactive({x:0,y:0,})// 方法function getPoint(event){console.log(event)point.xevent.clientX;point.yevent.clientY;}// 生命周期钩子函数onMounted((){window.addEventListener(click,getPoint);})onUnmounted((){window.removeEventListener(click,getPoint);})return point }需要引入hook的.vue文件 import {reactive,toRefs,onMounted,onUnmounted} from vueimport {getPoint} from ./hook/usePoint;export default {name: hook_component01,setup(){let pointgetPoint();return{...toRefs(point)}}}这个就是最简单hook的用法如果有知道export 导出函数和hook区别的大佬可以在下方评论区留言感激不敬 其它Composition API toRef与toRefs toRef 作用创建一个ref对象其value值指向另一个对象中的某个属性值语法const nametoRef(obj,name)应用要将响应式对象中的某个属性单独提供给外部使用时扩展toRefs与toRef功能一致但可以批量创建多个ref对象toRefs(obj) 示例一 templateh2toRef与toRefs/h2h2姓名{{person.name}}/h2h2年龄{{person.age}}/h2h2薪水{{person.job.salary}}k/h2button clickperson.name!修改姓名/buttonbutton clickperson.age修改年龄/buttonbutton clickperson.job.salary涨点薪资/button /templatescriptimport {reactive} from vueexport default {name: toRef_component,setup(){let personreactive({name:二郎神杨杨戬,age:18,job:{salary:20}})return{person,}}} /scriptstyle scoped/style示例一里面直接返回person对象导致每次取值的时候都需要person.xxx,这样既不美观也不优雅,修改一下代码。 示例二 templateh2toRef与toRefs/h2h2姓名{{name}}/h2h2年龄{{age}}/h2h2薪水{{salary}}k/h2button clickname!修改姓名/buttonbutton clickage修改年龄/buttonbutton clicksalary涨点薪资/button /templatescriptimport {reactive,toRef} from vueexport default {name: toRef_component,setup(){let personreactive({name:二郎神杨杨戬,age:18,job:{salary:20}})return{name:toRef(person,name),age:toRef(person,age),salary:toRef(person.job,salary)}}} /scriptstyle scoped/style错误用法示例一 templateh2toRef与toRefs/h2h2姓名{{name}}/h2h2年龄{{age}}/h2h2薪水{{salary}}k/h2button clickname!修改姓名/buttonbutton clickage修改年龄/buttonbutton clicksalary涨点薪资/button /templatescriptimport {reactive,toRef,toRefs} from vueexport default {name: toRef_component,setup(){let personreactive({name:二郎神杨杨戬,age:18,job:{salary:20}})return{name:person.name,age:person.age,salary:person.job.salary}}} /scriptstyle scoped/style错误用法示例二 templateh2toRef与toRefs/h2h2姓名{{name}}/h2h2年龄{{age}}/h2h2薪水{{salary}}k/h2h2peron对象{{person}}/h2button clickname!修改姓名/buttonbutton clickage修改年龄/buttonbutton clicksalary涨点薪资/button /templatescriptimport {reactive,toRef,toRefs,ref} from vueexport default {name: toRef_component,setup(){let personreactive({name:二郎神杨杨戬,age:18,job:{salary:20}})return{person,name:ref(person.name),age:ref(person.age),salary:ref(person.job.salary)}}} /scriptstyle scoped/styletoRefs 示例一 templateh2toRef与toRefs/h2h2姓名{{name}}/h2h2年龄{{age}}/h2h2薪水{{job.salary}}k/h2button clickname!修改姓名/buttonbutton clickage修改年龄/buttonbutton clickjob.salary涨点薪资/button /templatescriptimport {reactive,toRef,toRefs} from vueexport default {name: toRef_component,setup(){let personreactive({name:二郎神杨杨戬,age:18,job:{salary:20}})return{...toRefs(person)}}} /scriptstyle scoped/styleshallowRef和shallowReactive shallowReactive只处理对象最外层属性的响应式(浅响应式) shallowRef:只处理基本数据类型的响应式不进行对象的响应式处理 什么时候用 如果有一个对象数据结构比较深但变化时只是外层属性变化用shallowReactive如果有一个对象数据后续功能不会修改该对象中的属性而是生新的对象来替换用shallowRef shallowRef示例 templateh2shallowRef示例/h2h2当前sum的值是{{sum}}/h2button clicksum点我sum加1/buttonh2当前x.y的值是{{x.y}}/h2button clickx.y点我x.y加1/buttonbutton clickx{y:100}点我替换y的值/button /templatescriptimport {shallowRef,ref} from vueexport default {name: shallowRef_component,setup(){let sumref(0);let xshallowRef({y:0,})return{sum,x,}}} /scriptstyle scoped/style这里我们通过ref和shallowRef进行比对点击x.y加1按钮的时候视图不会触发更新因为y的值对象作为深层次的而直接点击sum加1的按钮的时候可以触发更新sum直接是浅层次的替换y的值的时候替换的是整个x的值即整个对象而不是x里面的值进行操作。 shallowReactive示例 templateh2shallowReactive示例/h2h2姓名{{name}}/h2h2年龄{{age}}/h2h2薪资{{job.salary}}k/h2button clickname!修改姓名/buttonbutton clickage修改年龄/buttonbutton clickjob.salary涨点薪资/button /templatescriptimport {shallowReactive,toRefs} from vueexport default {name: shallowReactive01_component,setup(){let personshallowReactive({name:张三,age:18,job:{salary:20,}})return{...toRefs(person)}}} /scriptstyle scoped/style点击修改姓名和修改年龄的按钮时可以看到视图发生变化点击涨薪的时候视图不会发生变化但是数据发生了变化这个大家可以使用控制台进行测试。 readonly和shallowReadonly readonly:让一个响应式数据变为只读的(深只读)shallowReadonly:让一个响应式变为只读的(浅只读) 示例一 templateh2readonly与shallowReadonly/h2h2姓名{{name}}/h2h2年龄{{age}}/h2h2薪资{{job.salary}}/h2h2当前sum的值是{{sum}}/h2button clickname!修改姓名/buttonbutton clickage修改年龄/buttonbutton clickjob.salary涨薪(readonly)/buttonbutton clickjob.salary涨薪(shallowReadonly)/buttonbutton clicksum点我加1/button /templatescriptimport {ref,reactive,readonly,shallowReadonly,toRefs} from vueexport default {name: shallowReadonly_component,setup(){let sumref(0);let personreactive({name:二郎神杨戬,age:21,job:{salary:200}});personreadonly(person);sumreadonly(sum);// personshallowReadonly(person);// sumreadonly(sum);return{sum,...toRefs(person)}}} /scriptstyle scoped/style使用readonly的时候按钮点击全部失效我们看下shallowReadonly的效果 使用shallowReadonly的时候修改姓名修改年龄都不会发生变化只有涨薪发生了变化 toRaw和markRaw toRaw 作用将一个由reactive生成的响应式对象转为普通对象使用场景用于读取响应式对象对应的普通对象对这个普通对象的所有操作不会引起页面更新 markRow 作用标记一个对象使其永远不会再成为响应式对象 应用场景 有些值不应该被设置为响应式的例如复杂的第三方类库 当渲染具有不可变的数据源的大列表时跳过响应式转换可以提高性能 示例一 templatediv stylewidth: 800px;margin: 0 autoh2toRaw与markRow/h2h2姓名{{name}}/h2h2年龄{{age}}/h2h2薪资{{job.salary}}k/h2button clickname!修改姓名/buttonbutton clickage修改年龄/buttonbutton clickjob.salary涨点薪资/buttonbutton clickshowRawPerson输出最原始的person对象/button/div /templatescriptimport {ref,reactive,toRaw,markRaw,toRefs} from vueexport default {name: toRaw01_component,setup(){let sumref(0);let personreactive({name:二郎神杨戬,age:18,job:{salary:20}})function showRawPerson() {let ptoRaw(person)console.log(p);let sumtoRaw(sum);console.log(sum); // 对ref定义的响应式数据无效}return{sum,...toRefs(person),showRawPerson}}} /scriptstyle scoped/style调用showRawPerson方法的时候控制台可以看到输出最原始的person,sum的话输出的undefinedtoRaw对ref定义的响应式数据无效接下来看下markRow的效果 示例二 templatediv stylewidth: 800px;margin: 0 autoh2toRaw与markRow/h2h2姓名{{name}}/h2h2年龄{{age}}/h2h2薪资{{job.salary}}k/h2button clickname!修改姓名/buttonbutton clickage修改年龄/buttonbutton clickjob.salary涨点薪资/buttonbutton clickshowRawPerson输出最原始的person对象/buttonh2车的信息是{{person.car}}/h2button clickaddCar给人添加一辆车/buttontemplate v-ifperson.carbutton clickperson.car.name!修改车名/buttonbutton clickperson.car.price修改车的价格/buttonbutton clickchangeCarPrice修改车的价格/button/template/div /templatescriptimport {ref,reactive,toRaw,markRaw,toRefs} from vueexport default {name: toRaw01_component,setup(){let sumref(0);let personreactive({name:二郎神杨戬,age:18,job:{salary:20}})function showRawPerson() {let ptoRaw(person)console.log(p);let sumtoRaw(sum);console.log(sum); // 对ref定义的响应式数据无效}function addCar() {let car{name:宝马,price:40}person.carmarkRaw(car);}function changeCarPrice() {person.car.price;console.log(person.car.price)}return{sum,person,...toRefs(person),showRawPerson,addCar,changeCarPrice}}} /scriptstyle scoped/style这里新增了一个车信息的方法和相关属性到person对象里面正常情况下直接在reactive里面的追加的数据会实现响应式的但是这里使用了markRaw方法所以点击修改车的名字和价格时数据发生了变化但是视图不会更新。 customRef 作用创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制,它需要一个工厂函数该函数接收 track 和 trigger 函数作为参数并且应该返回一个带有 get 和 set 的对象。 实现防抖效果 1、先实现自定义双向绑定 2、实现了双向绑定之后实现防抖 自定义双向绑定 templateh2customRef示例/h2input typetext v-modelmsgh2{{msg}}/h2 /templatescriptimport {customRef,} from vueexport default {name: customRef01_component,setup(){function myRef(msg){ // 自定义ref函数return customRef((track,trigger){return{get(){console.log(读取了值)track();return msg;},set(newValue){console.log(修改了值,修改后的值是${newValue});msgnewValue;trigger();}}})}let msgmyRef(你好);return{msg}}} /scriptstyle scoped/style在这里我们实现了数据的双向绑定接下来是实现防抖 templatediv stylewidth: 800px;margin: 0 autoh2customRef示例/h2input typetext v-modelmsgh2{{msg}}/h2/div /templatescriptimport {customRef,} from vueexport default {name: customRef01_component,setup(){function myRef(msg,delay){ // 自定义ref函数let timer;return customRef((track,trigger){return{get(){console.log(读取了值)track();return msg;},set(newValue){timersetTimeout((){console.log(修改了值,修改后的值是${newValue});msgnewValue;trigger();},delay)}}})}let msgmyRef(你好,500);return{msg}}} /scriptstyle scoped/style响应式数据的判断 isRef检查一个值是否为ref对象isReactive检查一个对象是否由reactive创建的响应式代理isReadonly检查一个对象是否由readonly创建的只读代理isProxy检查一个对象是否由reactive或者readonly方法创建的代理 provide和inject 作用实现祖与后代组件间通信套路父组件有一个provide选项来提供数据后代组件有一个inject选项来开始使用这些数据 祖组件 templateh2我是祖组件/h2h3汽车信息/h3p名称{{name}}/pp价格{{price}}/pinject_component/inject_component /templatescriptimport {reactive,toRefs,provide} from vueexport default {name: provide_component,setup(){let carreactive({name:宝马,price:40w});provide(car,car); // 提供providereturn{...toRefs(car)}}} /scriptstyle scoped/style后代组件 templateh2我是孙组件/h2h3汽车信息/h3p名称{{name}}/pp价格{{price}}/p /templatescriptimport {inject,toRefs,ref} from vueexport default {name: inject_component,setup(){let carinject(car); //使用inject接收return{...toRefs(car)}}} /scriptstyle scoped/styleFragment 在vue2中组件必需有一个根标签在vue3中组件可以没有根标签内部会将多个根标签包含在一个Fragment虚拟元素中 好处减少标签层级减少内存占用 Teleport Teleport是一种能够将我们的组件html结构移动到指定位置的技术 teleport to移动位置div v-ifisShow classmarkdiv classdialogh3我是一个弹窗/h3button clickisShowtrue关闭弹窗/button/div/div /teleport实现一个弹窗居中显示有四个组件分别为teleport_parentteleport_childteleport_sonteleport_dialog然后需要实现的效果是在teleport_son组件引入teleport_dialog组件teleport_dialog显示的时候在屏幕正中央 teleport_parent.vue templatediv classparenth2我是parent组件/h2teleport_child//div /templatescriptimport Teleport_child from ./teleport_child;export default {name: teleport_parent,components: {Teleport_child}} /scriptstyle scoped.parent{background-color: red;padding: 10px;} /styleteleport_child.vue templatediv classchildh2我是child组件/h2teleport_son//div /templatescriptimport Teleport_son from ./teleport_son;export default {name: teleport_child,components: {Teleport_son}} /scriptstyle scoped.child{background-color: orange;padding: 10px;} /styleteleport_son.vue templatediv classsonh2我是son组件/h2teleport_dialog//div /templatescriptimport Teleport_dialog from ./teleport_dialog;export default {name: teleport_son,components: {Teleport_dialog}} /scriptstyle scoped.son{background-color: yellow;padding: 10px;} /styleteleport_dialog.vue templatedivbutton clickisShowtrue点我弹窗/buttondiv classdialog_container v-ifisShowh2我是弹窗组件/h2div classdialog_bodyh2我是内容/h2h2我是内容/h2h2我是内容/h2h2我是内容/h2/divbutton clickisShowfalse关闭按钮/button/div/div /templatescriptimport {ref} from vueexport default {name: teleport_dialog,setup(){let isShowref(false);return{isShow}}} /scriptstyle scoped.dialog_container{width: 500px;height: 300px;background: red;} /style实现的效果如下 当我们点击按钮的时候效果是这样的 点击按钮的时候弹窗显示但是在son组件里面会改变son的高度这样子不太美观如果弹窗使用定位的话可以使其脱离文档流而不会撑开son里面的高度但是postion:absolute是根据最近的有定位元素的父元素进行定位的所以此方法不可靠我们需要实现的效果是根据body来定位 修改dialog的样式 templatedivbutton clickisShowtrue点我弹窗/buttonteleport tobodydiv classmask v-ifisShowdiv classdialog_containerh2我是弹窗组件/h2div classdialog_bodyh2我是内容/h2h2我是内容/h2h2我是内容/h2h2我是内容/h2/divbutton clickisShowfalse关闭按钮/button/div/div/teleport/div /templatescriptimport {ref} from vueexport default {name: teleport_dialog,setup(){let isShowref(false);return{isShow}}} /scriptstyle scoped.mask{position: absolute;top: 0px;left: 0px;right: 0px;bottom: 0px;background: rgba(0,0,0,.5);}.dialog_container{width: 500px;height: 300px;background: red;position: absolute;left: 50%;top: 50%;margin-left: -250px;margin-top: -150px} /styleSuspense 作用等待异步组件时渲染一些额外的内容让应用有更好的用户体验 使用步骤 异步引入组件 import {defineAsyncComponent} from vue const childdefineAsyncComponent(()import(./components/Child.vue))使用Suspense包裹组件并配置好default和fallback templatediv classapph3我是app组件/h3Suspensetemplate v-slot:defaultChild//templatetemplate v-slot:fallbackh3加载中....../h3/template/Suspense/div /template示例 suspense.vue templatediv classsuspenseh2我是suspense组件/h2child//div /templatescriptimport Child from ./child;export default {name: suspense01_component,components: {Child}} /scriptstyle scoped.suspense{background-color: red;padding: 10px;} /stylechild.vue templatediv classchildh2我是child组件/h2/div /templatescriptexport default {name: child} /scriptstyle scoped.child{background-color: orange;padding: 10px;} /style如果使用以上代码引入的话那么suspense和child组件将会同时加载如果child里面还有其它子组件的话子组件里面还有子组件在网络慢的情况下用户可能就看不到child组件以及child里面其它的组件。这会影响用户体验修改对应的代码 suspense.vue templatediv classsuspenseh2我是suspense组件/h2suspensetemplate v-slot:defaultchild//templatetemplate v-slot:fallbackh3请稍等加载中。。。/h3/template/suspense/div /templatescript// import Child from ./child; // 静态引入import {defineAsyncComponent} from vueconst ChilddefineAsyncComponent(()import(./child)) // 异步引入export default {name: suspense01_component,components: {Child}} /scriptstyle scoped.suspense{background-color: red;padding: 10px;} /stylechild.vue templatediv classchildh2我是child组件/h2h2当前sum的值是{{sum}}/h2/div /templatescriptimport {ref} from vueexport default {name: child,setup(){let sumref(0);return new Promise((resole,reject){setTimeout((){resole({sum});},3000)})}} /scriptstyle scoped.child{background-color: orange;padding: 10px;} /style为了看到效果延迟了3秒之后显示组件 Vue2x和Vue3x的其它变化 1.全局API的转移 vue2.x有许多全局API和配置例如全局注册组件、注册全局指令等 注册全局组件 Vue.component(MyButton,{data:(){count:0,},template:button clickcountclicked {{count}} times/button});注册全局指令 Vue.directive(focus,{inserted:elel.foucus })Vue3.0中对这些API做出了调整 将全局的API,即Vue.xxx调整到应用实例app上 2.x 全局API(Vue)3.x 实例API(app)Vue.config.xxxapp.config.xxxVue.config.productionTip移除Vue.componentapp.componentVue.directiveapp.directiveVue.mixinapp.mixinVue.useapp.useVue.prototypeapp.config.globalProperties 2.其它改变 data 选项应始终被声明为一个函数过度类的写法 Vue2.x的写法 .v-enter .v-leave-to{opacity:0, } v-leave, v-enter-to{opacity:1 }Vue3.x的写法 .v-enter-from, .v-leave-to{opacity:0 } .v-leave-to .v-enter-to{opacity:1 }移除keyCode作为v-on的修饰符同时也不再支持config.keyCodes移除v-on.navtive修饰符 父组件绑定事件 my-componentv-on:closehandleComponentEvent v-on:clickhandleNativeClickEvent /my-component子组件中声明自定义组件 export default{emits:[close] }移除过滤器(filter) 过滤器虽然看起来方便但它需要一个自定义语法打破大括号内表达式只是javascript的假设这不仅有学习成本而且有实现成 本 建议用法调用或者计算属性去替换过滤器 Vue3x相关资料 相关库名称在线地址Vue 3.0 官方文档(英文)在线地址Vue 3.0 中文文档在线地址Composition-API手册在线地址Vue 3.0 源码学习在线地址Vue-Router 官方文档在线地址Vuex 4.0Githubvue-devtoolsGithubVite 源码学习线上地址Vite 2.0 中文文档线上地址Vue3 新动态线上地址 Vue3xUI组件库 Element-plus 仓库地址GitHub - element-plus/element-plus: A Vue.js 3 UI Library made by Element team 文档地址https://element-plus.gitee.io/zh-CN/ 开源项目 Vue 3.0 Vite 2.0 Vue-Router 4.0 Element-Plus Echarts 5.0 Axios 开发的后台管理系统GitHub - newbee-ltd/vue3-admin: Vue 3 Vite 2 Vue-Router 4 Element-Plus Echarts 5 Axios 开发的后台管理系统Vue3.0TypeScriptNodeJSMySql编写的一套后台管理系统GitHub - pure-admin/vue-pure-admin: 全面ESMVue3ViteElement-PlusTypeScript编写的一款后台管理系统兼容移动端 Ant Design of Vue 仓库地址GitHub - vueComponent/ant-design-vue: An enterprise-class UI components based on Ant Design and Vue. 文档地址Ant Design Vue — An enterprise-class UI components based on Ant Design and Vue.js 开源项目 AntdV后台管理系统GitHub - iczer/vue-antd-admin: Ant Design Pros implementation with Vuevue3.x ant-design-vuebeta 版本免费商用支持 PC、平板、手机GitHub - zxwk1998/vue-admin-better: vue admin,vue3 admin,vue3.0 admin,vue后台管理,vue-admin,vue3.0-admin,admin,vue-admin,vue-element-admin,ant-design,vab admin pro,vab admin plus,vue admin plus,vue admin pro基于 Vue3.0 Vite Ant Design VueGitHub - lirongtong/miitvip-vue-admin-manager: :kissing_heart:A unified template used to backend management built on Vue3.x Vite Ant Design Vue Vite. Makeit Admin Pro是基于 Vue3.x Vite Ant Design Vue 组件库开发的一套适合中后台管理项目的统一 UI 框架包含页面布局 / 注册 / 登录 / 验证码等常用模块npm 安装开箱即用。持续开发更新中 ... Vant 仓库地址GitHub - youzan/vant: A lightweight, customizable Vue UI library for mobile web apps. 文档地址https://vant-contrib.gitee.io/vant/#/zh-CN/ 开源项目 newbee-mall Vue3 版本GitHub - newbee-ltd/newbee-mall-vue3-app: Vue3 全家桶 Vant 搭建大型单页面商城项目新蜂商城 Vue3.2 版本技术栈为 Vue3.2 Vue-Router4.x Pinia Vant4.x。高仿微信笔记本GitHub - Nick930826/daily-cost: Vue 3.0 Vant 3.0 Vue-Router 4.0 高仿微信记账本 H5 项目。仿京东淘宝电商GitHub - geekskai/vue3-jd-h5: :fire: Based on vue3.x,vite5.x, vant3.0.0, vue-router v4.0.0-0, vuex^4.0.0-0, vue-cli3, mockjs, imitating Jingdong Taobao, mobile H5 e-commerce platform! 基于vue3.0.0 ,vant3.0.0,vue-router v4.0.0-0, vuex^4.0.0-0,vue-cli3,mockjs,仿京东淘宝的,移动端H5电商平台! NutUI 3 仓库地址GitHub - jdf2e/nutui: 京东风格的移动端 Vue 组件库支持多端小程序(A Vue.js UI Toolkit for Mobile Web)文档地址NutUI - 移动端组件库 Vue3X相关视频 相关库名称在线地址Vue 3.0 实战星座物语 H5 项目在线地址Vue 3.0 UI 组件库开发在线地址Vue 3.0 Vite 手册阅读在线地址Vue 3.0 入门之项目搭建杨村长在线地址Vue 3.0 入门技术胖【不太建议推荐】在线地址Vite 2.0 插件开发指南在线地址Vue 3.0 Vite 2.0 快速搭建 Electron 应用在线地址Vue3.0视频(强烈推荐)在线地址Vue3.0驾照题库在线地址 参考资料 掘金地址https://juejin.cn/post/6955129410705948702 Vue.js官网https://v3.cn.vuejs.org/ bilibili地址尚硅谷Vue2.0Vue3.0全套教程丨vuejs从入门到精通_哔哩哔哩_bilibili MDN地址MDN Web Docs ES6地址ES6 入门教程 总结 总体来说学完这篇博客基本涵盖了Vue3中的绝大部分内容写的也特别详细笔记配套资料和实战视频都有主要看你愿不愿意抽出时间去学习我自己认为学习是一辈子的事情当然学习也是一件特别孤独的事情。如果本篇文章对您有所帮助的话记得点赞、收藏和关注原创不易三连支持感谢大家
http://www.w-s-a.com/news/641077/

相关文章:

  • 优秀企业建站织梦能不能做门户网站
  • 广东省建设局官方网站wordpress 自动安装 插件怎么用
  • 哪类小网站容易做h5页面制作代码
  • 北京网站建设公司华网百度热搜seo
  • 小清新博客网站中山做网站公司
  • 美团做团购网站如何新建自己的网站
  • 安卓软件制作网站电子商务网站建设实训总结报告
  • 肃宁网站制作价格外国设计师素材网站
  • 自已建网站用jsp做的可运行的网站
  • 外贸建站代理网站建设设计公司哪家好
  • 普升高端品牌网站建设台州中兴建设咨询有限公司网站
  • 模板演示网站移动网站开发公司
  • 网站管理办法制度公司招聘信息
  • 宜昌市建设监理协会网站免备案免费域名
  • 河北省建设银行网站首页备案号怎么放到网站
  • 做电脑网站用什么软件有哪些wordpress版权修改
  • 加强部门网站建设工作wordpress文章页横幅
  • 中英网站怎么做wordpress本地音乐
  • 万网提供的网站建设服务的具体项目祥云平台网站建设
  • ftp网站怎么看后台的代码网站 制作软件
  • 网站开发软件教程网站tag 怎么实现
  • 中国建设监理协会化工监理协会网站彩票站自己做网站吗
  • 170个可带链接锚文本外链的网站论坛微信上如何创建小程序
  • 用js来做网站亳州建设局网站
  • 做网站的公司利润多少呢纺织厂网站模板
  • 网页设计构建的基本流程宜宾seo网站建设
  • 西安网站开发公司价格保定徐水网站建设
  • 学做川菜下什么网站软件著作权和专利的区别
  • 百度网站标题东莞外包公司有哪些
  • 织梦增加网站英文名称网页界面设计特点