大连企业网站制作,wordpress支持手机版,腾讯cdn WordPress,网站开发需求文件一、前端框架的由来 1、服务端渲染
sequenceDiagram
浏览器-服务器: https://www.bilibili.com/
Note right of 服务器: 组装页面(服务端渲染)
服务器--浏览器: 完整页面2、前后端分离 sequenceDiagram
浏览器-服务器: https://www.bilibili.com/
服务…
一、前端框架的由来 1、服务端渲染
sequenceDiagram
浏览器-服务器: https://www.bilibili.com/
Note right of 服务器: 组装页面(服务端渲染)
服务器--浏览器: 完整页面2、前后端分离 sequenceDiagram
浏览器-服务器: https://www.bilibili.com/
服务器-浏览器: 无内容的html
activate 浏览器
浏览器--服务器: ajax
服务器--浏览器: 各种业务数据
Note left of 浏览器: 运行js创建元素渲染页面
deactivate 浏览器3、单页应用
sequenceDiagram
浏览器-服务器: https://www.bilibili.com/
服务器-浏览器: 无内容的html
activate 浏览器
Note left of 浏览器: 运行js创建元素渲染页面
浏览器--服务器: ajax
服务器--浏览器: 各种业务数据
Note left of 浏览器: 跳转页面
浏览器--服务器: ajax
服务器--浏览器: 各种业务数据
Note left of 浏览器: JS重新构建页面元素
deactivate 浏览器4、vue
sequenceDiagram
浏览器-服务器: https://www.bilibili.com/
服务器-浏览器: 无内容的html
activate 浏览器
Note left of 浏览器: 运行包含vue的js使用框架渲染页面
浏览器--服务器: ajax
服务器--浏览器: 各种业务数据
Note left of 浏览器: 使用vue-router跳转页面
deactivate 浏览器二、vue的一些底层原理 1、注入 vue会将以下配置注入到vue实例 data和界面相关的数据computed通过已有数据计算得来的数据methods方法 模板中可以使用vue实例中的成员 2、虚拟DOM树
直接操作真实的DOM会引发严重的效率问题vue使用虚拟DOMvnode的方式来描述要渲染的内容vnode是一个普通的js对象用于描述界面上应该有什么比如 var vnode {tag: h1,children: [{tag: undefined,text: Hello}]}上面的对象描述了 有一个标签名为h1的节点它有一个子节点该子节点是一个文本内容为【Hello】 vue模板并不是真实的DOM它会编译为虚拟DOM
div idapph1第一个vue应用{{title}}/h1p作者{{author}}/p
/div上面的模板会被编译为类似下面结构的虚拟DOM
var vnode {tag: div,children: [{tag: h1,children: [{text: 第一个vue应用Hello World}]}, {tag: p,children: [{text: 作者siuser}]}]}虚拟DOM树会最终成为真实的DOM树
当数据变化后将引发重新渲染vue会比较新旧两棵vnode tree找出差异然后仅把差异部分应用到真实dom tree中 可见在vue中要得到最终的界面必须要生成一个vnode tree
vue通过以下逻辑生成vnode tree 注意虚拟节点树必须是单根的 3、挂载
将生成的真实DOM树放置到某个元素位置称之为挂载
挂载的方式
通过el:css选择器进行配置通过vue实例.$mount(“css选择器”)进行配置 4、完整流程 三、组件开发 1、创建组件
组件是根据一个普通的配置对象创建的所以要开发一个组件只需要写一个配置对象即可
该配置对象和vue实例的配置是几乎一样的
//组件配置对象
var myComp {data(){return {// ...}},template: ....
}值得注意的是组件配置对象和vue实例有以下几点差异
无eldata必须是一个函数该函数返回的对象作为数据由于没有el配置组件的虚拟DOM树必须定义在template或render中 2、注册组件
注册组件分为两种方式一种是全局注册一种是局部注册 2.1、全局注册
一旦全局注册了一个组件整个应用中任何地方都可以使用该组件 全局注册的方式是 // 参数1组件名称将来在模板中使用组件时会使用该名称
// 参数2组件配置对象
// 该代码运行后即可在模板中使用组件
Vue.component(my-comp, myComp)在模板中可以使用组件了
my-comp /
!-- 或 --
my-comp/my-comp但在一些工程化的大型项目中很多组件都不需要全局使用 比如一个登录组件只有在登录的相关页面中使用如果全局注册将导致构建工具无法优化打包 因此除非组件特别通用否则不建议使用全局注册 2.2、局部注册
局部注册就是哪里要用到组件就在哪里注册 局部注册的方式是在要使用组件的组件或实例中加入一个配置
// 这是另一个要使用my-comp的组件
var otherComp {components:{// 属性名为组件名称模板中将使用该名称// 属性值为组件配置对象my-comp: myComp},template: div!-- 该组件的其他内容 --my-comp/my-comp/div;
}3、应用组件
在模板中使用组件特别简单把组件名当作HTML元素名使用即可但要注意以下几点
组件必须有结束
组件可以自结束也可以用结束标记结束但必须要有结束下面的组件使用是错误的
my-comp组件的命名
无论你使用哪种方式注册组件组件的命名需要遵循规范。
组件可以使用kebab-case 短横线命名法也可以使用PascalCase 大驼峰命名法
下面两种命名均是可以的
var otherComp {components:{my-comp: myComp, // 方式1MyComp: myComp //方式2}
}实际上使用小驼峰命名法 camelCase也是可以识别的只不过不符合官方要求的规范 使用PascalCase方式命名还有一个额外的好处即可以在模板中使用两种组件名
var otherComp {components:{MyComp: myComp}
}模板中
!-- 可用 --
my-comp /
MyComp /因此在使用组件时为了方便往往使用以下代码
var MyComp {//组件配置
}var OtherComp {components:{MyComp // ES6速写属性}
}四、组件树
一个组件创建好后往往会在各种地方使用它。它可能多次出现在vue实例中也可能出现在其他组件中
于是就形成了一个组件树 五、向组件传递数据基础 大部分组件要完成自身的功能都需要一些额外的信息 比如一个头像组件需要告诉它头像的地址这就需要在组件时向组件传递数据 传递数据的方式有很多种很常见的一种是使用组件属性component props 首先在组件中申明可以接收哪些属性 var MyComp {props:[p1, p2, p3],// 和vue实例一样使用组件时也会创建组件的实例// 而组件的属性会被提取到组件实例中因此可以在模板中使用template: div{{p1}}, {{p2}}, {{p3}}/div
}在使用组件时向其传递属性
var OtherComp {components: {MyComp},data(){return {a:1}},template: my-comp :p1a :p22 p33/
}注意在组件中属性是只读的绝不可以更改这叫做单向数据流 六、v-if和v-show 面试题v-if和v-show有什么区别 v-if能够控制是否生成vnode也就间接控制了是否生成对应的dom。 当v-if为true时会生成对应的vnode并生成对应的dom元素当其为false时不会生成对应的vnode自然不会生成任何的dom元素 v-show始终会生成vnode也就间接导致了始终生成dom。 它只是控制dom的display属性当v-show为true时不做任何处理当其为false时生成的dom的display属性为none 使用v-if可以有效的减少树的节点和渲染量但也会导致树的不稳定 而使用v-show可以保持树的稳定但不能减少树的节点和渲染量 因此在实际开发中显示状态变化频繁的情况下应该使用v-show以保持树的稳定 显示状态变化较少时应该使用v-if以减少树的结点和渲染量 七、组件事件 抛出事件子组件在某个时候发生了一件事但自身无法处理于是通过事件的方式通知父组件处理事件参数子组件抛出事件时传递给父组件的数据注册事件父组件申明当子组件发生某件事的时候自身将做出一些处理 八、插槽 在某些组件模板中有一部分区域需要父组件来指定 !-- message组件一个弹窗消息 --
div classmessage-containerdiv classcontent!-- 这里是消息内容可以是一个文本也可能是一段html具体是什么不知道需要父组件指定 --/divbutton确定/buttonbutton关闭/button
/div1、插槽的简单用法
此时就需要使用插槽来定制组件的功能
!-- message组件一个弹窗消息 --
div classmessage-containerdiv classcontent!-- slot是vue的内置组件 --slot/slot/divbutton确定/buttonbutton关闭/button
/div!-- 父组件App --
Messagediv classapp-messagepApp Message/pa hrefdetail/a/div
/Message!-- 最终的结果 --
div classmessage-containerdiv classcontentdiv classapp-messagepApp Message/pa hrefdetail/a/div/divbutton确定/buttonbutton关闭/button
/div2、具名插槽 如果某个组件中需要父元素传递多个区域的内容也就意味着需要提供多个插槽 为了避免冲突就需要给不同的插槽赋予不同的名字 !-- Layout 组件 --
div classlayout-containerheader!-- 我们希望把页头放这里提供插槽名为header --slot nameheader/slot/headermain!-- 我们希望把主要内容放这里提供插槽名为default --slot/slot/mainfooter!-- 我们希望把页脚放这里提供插槽名为footer --slot namefooter/slot/footer
/div!-- 父组件App --
BaseLayouttemplate v-slot:headerh1Here might be a page title/h1/templatetemplate v-slot:defaultpA paragraph for the main content./ppAnd another one./ptemplate v-slot:defaulttemplate v-slot:footerpHeres some contact info/p/template
/BaseLayout九、路由
vue-router官网https://router.vuejs.org/zh/ 1、路由插件
npm i vue-router路由插件的使用
import Vue from vue
import VueRouter from vue-routerVue.use(VueRouter); // Vue.use(插件) 在Vue中安装插件const router new VueRouter({// 路由配置
})
new Vue({...,router
})2、基本使用
// 路由配置
const router new VueRouter({routes: [ // 路由规则// 当匹配到路径 /foo 时渲染 Foo 组件{ path: /foo, component: Foo },// 当匹配到路径 /bar 时渲染 Bar 组件{ path: /bar, component: Bar }]
})!-- App.vue --
div classcontainerdiv!-- 公共区域 --/divdiv!-- 页面区域 --!-- vue-router 匹配到的组件会渲染到这里 --RouterView //div
/div3、路由模式
路由从哪里获取访问路径路由如何改变访问路径
vue-router提供了三种路由模式
hash默认值。路由从浏览器地址栏中的hash部分获取路径改变路径也是改变的hash部分。该模式兼容性最好
http://localhost:8081/#/blog -- /blog
http://localhost:8081/about#/blog -- /bloghistory路由从浏览器地址栏的location.pathname中获取路径改变路径使用的H5的history api。该模式可以让地址栏最友好但是需要浏览器支持history api
http://localhost:8081/#/blog -- /
http://localhost:8081/about#/blog -- /about
http://localhost:8081/blog -- /blogabstract路由从内存中获取路径改变路径也只是改动内存中的值。这种模式通常应用到非浏览器环境中
内存 / -- /
内存 /about -- /about
内存 /blog -- /blog4、导航
vue-router提供了全局的组件RouterLink它的渲染结果是一个a元素
RouterLink to/blog文章/RouterLink
!-- mode:hash 生成 --
a href#/blog文章/a
!-- mode:history 生成 --
!-- 为了避免刷新页面vue-router实际上为它添加了点击事件并阻止了默认行为在事件内部使用hitory api更改路径 --
a href/blog文章/a激活状态 默认情况下vue-router会用当前路径匹配导航路径 如果当前路径是以导航路径开头则算作匹配会为导航的a元素添加类名router-link-active如果当前路径完全等于导航路径则算作精确匹配会为导航的a元素添加类名router-link-exact-active
例如当前访问的路径是/blog则
导航路径类名/router-link-active/blogrouter-link-active router-link-exact-active/about无/message无
可以为组件RouterLink添加bool属性exact将匹配规则改为必须要精确匹配才能添加匹配类名router-link-active
例如当前访问的路径是/blog则
导航路径exact类名/true无/blogfalserouter-link-active router-link-exact-active/abouttrue无/messagetrue无
例如当前访问的路径是/blog/detail/123则
导航路径exact类名/true无/blogfalserouter-link-active/abouttrue无/messagetrue无
另外可以通过active-class属性更改匹配的类名通过exact-active-class更改精确匹配的类名 5、命名路由
使用命名路由可以解除系统与路径之间的耦合
// 路由配置
const router new VueRouter({routes: [ // 路由规则// 当匹配到路径 /foo 时渲染 Foo 组件{ name:foo, path: /foo, component: Foo },// 当匹配到路径 /bar 时渲染 Bar 组件{ name:bar, path: /bar, component: Bar }]
})!-- 向to属性传递路由信息对象 RouterLink会根据你传递的信息以及路由配置生成对应的路径 --
RouterLink :to{ name:foo }go to foo/RouterLink十、vue的一些边角知识 1、css module
当在main.js使用纯dom操作需要引用css module
需要将样式文件命名为xxx.module.oooxxx为文件名
ooo为样式文件后缀名可以是css、less
// 测试一下纯DOM操作
import styles from ./styles/message.module.less;
console.log(styles);
const div document.createElement(div);
div.className styles.message;
div.innerText asdfasdf;
document.body.appendChild(div);2、得到组件渲染的Dom /**获取某个组件渲染的Dom根元素*/
function getComponentRootDom(comp, props){const vm new Vue({render: h h(comp, {props})})vm.$mount();return vm.$el;
}3、扩展vue实例 prototype的知识点延申 4、ref
templatedivp refparasome paragraph/pChildComp refcomp /button clickhandleClick查看所有引用/button/div
/templatescriptimport ChildComp from ./ChildCompexport default {components:{ChildComp},methods:{handleClick(){// 获取持有的所有引用console.log(this.$refs);/*{para: p元素原生DOM,comp: ChildComp的组件实例}*/}}}
/script通过ref可以直接操作dom元素甚至可能直接改动子组件这些都不符合vue的设计理念。 除非迫不得已否则不要使用ref 十一、远程获取数据的意义 1、开发环境有跨域问题
sequenceDiagram
浏览器-前端开发服务器: http://localhost:8080/
前端开发服务器-浏览器: 页面
浏览器-后端测试服务器: ajax 跨域http://test-data:3000/api/news
后端测试服务器-浏览器: JSON数据
rect rgb(224,74,74)
Note right of 浏览器: 浏览器阻止数据移交
end2、生产环境没有跨域问题
sequenceDiagram
浏览器-服务器: http://www.my-site.com/
服务器-浏览器: 页面
浏览器-服务器: ajaxhttp://www.my-site.com/api/news
服务器-浏览器: JSON数据sequenceDiagram
浏览器-静态资源服务器: http://www.my-site.com/
静态资源服务器-浏览器: 页面
浏览器-数据服务器: ajax 跨域http://api.my-site.com/api/news
数据服务器-浏览器: [允许www.my-site.com]JSON数据3、解决开发环境的跨域问题
sequenceDiagram
浏览器-前端开发服务器: http://localhost:8080/
前端开发服务器-浏览器: 页面
浏览器-前端开发服务器: ajaxhttp://localhost:8080/api/news
前端开发服务器-后端测试服务器: 代理请求http://test-data:3000/api/news
后端测试服务器-前端开发服务器: JSON数据
前端开发服务器-浏览器: JSON数据4、为什么要用mock数据
sequenceDiagram
浏览器-前端开发服务器: http://localhost:8080/
前端开发服务器-浏览器: 页面
浏览器-前端开发服务器: ajaxhttp://localhost:8080/api/news
前端开发服务器-后端测试服务器: 代理请求http://test-data:3000/api/news
后端测试服务器-前端开发服务器: 404 后端正在开发中
前端开发服务器-浏览器: 404sequenceDiagram
participant 浏览器
participant MockJS
participant 前端开发服务器
activate MockJS
Note left of MockJS: 定义ajax拦截规则
deactivate MockJS
浏览器-前端开发服务器: http://localhost:8080/
前端开发服务器-浏览器: 页面
浏览器-MockJS: ajaxhttp://localhost:8080/api/news
MockJS-浏览器: 模拟的JSON数据十二、组件生命周期 1、常见应用 1.1、加载远程数据
export default {data(){return {news: []}},async created(){this.news await getNews();}
}1.2、直接操作DOM
export default {data(){return {containerWidth:0,containerHeight:0}},mounted(){this.containerWidth this.$refs.container.clientWidth;this.containerHeight this.$refs.container.containerHeight;}
}1.3、启动和清除计时器
export default {data(){return {timer: null}},created(){this.timer setInterval((){... }, 1000)},destroyed(){clearInterval(this.timer); }
}十三、自定义指令 1、全局定义
// 指令名称为mydirec1
Vue.directive(mydirec1, {// 指令配置
})// 指令名称为mydirec2
Vue.directive(mydirec2, {// 指令配置
})之后所有的组件均可以使用mydirec1和mydirec2指令
template!-- 某个组件代码 --divMyComp v-mydirec1js表达式 /div v-mydirec2js表达式.../divimg v-mydirec1js表达式 //div
/template2、局部定义
局部定义是指在某个组件中定义指令和局部注册组件类似。
定义的指令仅在该组件中有效。
template!-- 某个组件代码 --divMyComp v-mydirec1js表达式 /div v-mydirec2js表达式.../divimg v-mydirec1js表达式 //div
/templatescript
export default {// 定义指令directives: {// 指令名称mydirec1mydirec1: {// 指令配置},// 指令名称mydirec2mydirec2: {// 指令配置}}
}
/script和局部注册组件一样为了让指令更加通用通常我们会把指令的配置提取到其他模块。
template!-- 某个组件代码 --divMyComp v-mydirec1js表达式 /div v-mydirec2js表达式.../divimg v-mydirec1js表达式 //div
/templatescript// 导入当前组件需要用到的指令配置对象import mydirec1 from /directives/mydirec1;import mydirec2 from /directives/mydirec2;export default {// 定义指令directives: {mydirec1,mydirec2}}
/script指令配置对象
没有配置的指令就像没有配置的组件一样毫无意义
vue支持在指令中配置一些钩子函数在适当的时机vue会调用这些钩子函数并传入适当的参数以便开发者完成自己想做的事情。
常用的钩子函数
// 指令配置对象
{bind(){// 只调用一次指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。},inserted(){// 被绑定元素插入父节点时调用。},update(){// 所在组件的 VNode 更新时调用}
}查看更多的钩子函数 每个钩子函数在调用时vue都会向其传递一些参数其中最重要的是前两个参数
// 指令配置对象
{bind(el, binding){// el 是被绑定元素对应的真实DOM// binding 是一个对象描述了指令中提供的信息}
}bingding 对象 查看更多bingding对象的属性 配置简化
比较多的时候在配置自定义指令时我们都会配置两个钩子函数
{bind(el, bingding){},update(el, bingding){}
}这样在元素绑定和更新时都能运行到钩子函数
如果这两个钩子函数实现的功能相同可以直接把指令配置简化为一个单独的函数
function(el, bingding){// 该函数会被同时设置到bind和update中
}利用上述知识可满足大部分自定义指令的需求 更多的自定义指令用法见官网