모바일 웹 개발에서는 vue를 사용할 수 있습니다. Vue는 단일 페이지 애플리케이션을 개발할 수 있는 오픈 소스 JavaScript 프레임워크로, 웹 애플리케이션 프레임워크로 사용할 수 있으며 웹 개발을 단순화하도록 설계되었습니다. Vue는 모바일 개발을 지원하며 모바일 웹 앱 제작에 적합합니다. 진입 비용이 저렴하고 시작이 빠릅니다. i-view 및 Element UI와 같은 성숙한 프런트엔드 UI 라이브러리와 함께 개발할 수 있습니다.
이 튜토리얼의 운영 환경: windows7 시스템, vue3 버전, DELL G3 컴퓨터.
Vue.js는 단일 페이지 애플리케이션을 개발할 수 있는 오픈 소스 JavaScript 프레임워크입니다. 웹 애플리케이션 프레임워크로도 사용할 수 있으며 웹 개발을 단순화하도록 설계되었습니다. Vue.js 애플리케이션 개발은 놀라운 웹 애플리케이션을 구축하기 위해 전 세계 개발자로부터 많은 관심을 끌었습니다.
Vue.js는 여러 가지 이유로 인기가 있으며, 주요 이유 중 하나는 아무런 조치 없이 다시 렌더링할 수 있는 능력입니다. 재사용 가능하고 작지만 강력한 구성 요소를 구축할 수 있으므로 필요할 때 구성 요소를 추가할 수 있는 구성 가능한 프레임워크를 제공합니다.
vue.js는 모바일 개발을 지원하며 모바일 웹앱 제작에 적합합니다. 모바일 단말기의 경우 vue가 첫 번째 선택입니다. 진입 비용이 저렴하고 시작이 빠릅니다. i-view 및 Element UI와 같은 일부 성숙한 프런트엔드 UI 라이브러리와 함께 개발할 수 있습니다.
앱은 매우 간단합니다. 말할 필요도 없이 구성 요소 개발 모듈 관리는 vue-loader와 webpack을 사용합니다. 앱 개발 과정에서는 주의하면 됩니다. 또한 다양한 UI 라이브러리와 함께 사용하면 애플리케이션을 더욱 아름답게 만들 수 있습니다. SUI 또는 Framework7을 사용할 수 있습니다. 이후에는 jQuery를 사용할 수 있으므로 Framework7의 분기인 Light7을 사용합니다. 추가 [관련 추천 : vuejs 입문 튜토리얼]
모바일 단말기 적응
PC 단말기에 비해 모바일 단말기의 해상도는 별롭니다. 모든 개발자에게 모바일 단말기 적응은 모바일 단말기 개발의 핵심입니다.
모바일 측면에서는 헤드 태그에서 다음 코드를 자주 볼 수 있습니다.
<meta name='viewport' content='width=device-width,initial-scale=1,user-scale=no' />
메타 태그를 통한 뷰포트의 크기는 페이지의 확대/축소 비율을 정의합니다. 먼저 여러 뷰포트 너비의 의미를 알아야 합니다.
layoutviewport
레이아웃 너비는 웹 페이지의 너비layoutviewport
布局宽度,就是网页的宽度visualviewport
可是宽度,就是浏览器窗口的宽度,这个值决定了我们手机一屏能看到的内容;visualviewport
和layoutviewport
的大小关系,决定了是否会出现滚动条,当visualviewport
更大或者刚好等于layoutviewport
时是不会出现滚动条的。idealviewport
为浏览器定义的可完美适配移动端的viewport,固定不变,可以认为是设备视口宽度device-width
。meta的设置其实就是对layoutviewport
和visualviewport
进行设置。
width=device-width
表示页面宽度layoutviewport
与设备视口宽度idealviewport
一致initial-scale=1
表示页面宽度和网页宽度与设备视口宽度的初始缩放比例,visualviewport
由这个比例决定,但是对于layoutviewport
来说,它同时受到两个属性的影响,然后取其中较大的那个值。user-scale=no
禁止缩放所以现在我们知道,这段在移动端常见的代码的意思,即将visualviewport
和layoutviewport
设置为idealviewport
visualviewport
그러나 너비는 브라우저 창입니다. Visualviewport가 layoutviewport
보다 크거나 같을 때 스크롤 막대는 idealviewport
는 모바일 단말에 완벽하게 적응할 수 있는 브라우저에 의해 정의된 뷰포트이며 고정되어 있으며 장치 뷰포트 너비 device-width</code)로 간주될 수 있습니다. >. <p><span style="font-size: 14px;"><strong>meta의 설정은 실제로 <code>layoutviewport
및 visualviewport
를 설정하는 것입니다. 페이지 너비 layoutviewport
는 기기 뷰포트 너비 idealviewport
와 일치합니다. initial-scale= 1
은 페이지 너비와 웹 페이지의 초기 배율 비율을 나타냅니다. 장치 뷰포트 너비에 대한 visualviewport
는 이 비율에 따라 결정되지만 layoutviewport
의 경우에도 두 가지 속성의 영향을 받아 더 큰 값을 취합니다. user-scale=no
스케일링 비활성화visualviewport
및 layoutviewport</code의 의미를 알았습니다. >는 <code>idealviewport
의 값으로 설정됩니다. 이렇게 하면 모바일 단말기에 스크롤 막대가 없어져 웹 콘텐츠가 더 잘 표시될 수 있습니다. 페이지. document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';
视口单位
将视口宽度window.innerWidth
和视口高度window.innerHeight
(即layoutviewport
)等分为 100 份。
vw : 1vw 为视口宽度的 1% vh : 1vh 为视口高度的 1% vmin : vw 和 vh 中的较小值 vmax : 选取 vw 和 vh 中的较大值
和rem相比较,视口单位不需要使用js对根元素进行设置,兼容性稍差,但是大部分设备都已经支持了,同样的无须再开发时进行单位换算,直接使用相关的插件postcss-px-to-viewport在输出的时候进行转换。
修改viewport
之前我们提到了layoutviewport
布局宽度实际上不是一个固定值,而是通过meta设置属性,通过idealviewport
计算出来的值,我们可以通过控制meta的属性来将layoutviewport
固定为某一个值。一般设计图的宽度为750px,现在我们的目标就是将layoutviewport
设置为750px;layoutviewport
受到两个属性的影响,width属性我们之间设置为750,initial-scale缩放比例应该为idealviewport
的宽度/750;当我们未改变meta标签属性的时候,layoutviewport
的值其实就是idealviewport
的值,所以可以通过document.body.clientWidth
或者window.innerWidth
来获取。
;(function () { const width = document.body.clientWidth || window.innerWidth const scale = width / 750 const content = 'width=750, initial-scale=' + scale + ', minimum-scale=' + scale + ', maximum-scale=' + scale + ', viewport-fit=cover' document.querySelector('meta[name="viewport"]').content = content })()
设置完成之后,layoutviewport
在不同的设备中会始终保持为750px,我们开发时可以直接使用设计稿尺寸。
布局样式
布局的方式可以是各种各样的,但是出于兼容性的考虑,有些样式我们最好避免使用,难以解决的问题,那就不去面对。
需要谨慎对待的fixed
position:fixed
在日常的页面布局中非常常用,在许多布局中起到了关键的作用。它的作用是:position:fixed
的元素将相对于屏幕视口(viewport)的位置来指定其位置。并且元素的位置在屏幕滚动时不会改变。
但是,在许多特定的场合,position:fixed
的表现与我们想象的大相径庭。
iOS弹出键盘;软键盘唤起后,页面的 fixed元素将失效(iOS认为用户更希望的是元素随着滚动而移动,也就是变成了 absolute 定位),既然变成了absolute,所以当页面超过一屏且滚动时,失效的 fixed 元素就会跟随滚动了。
当元素祖先的 transform 属性非 none 时,定位容器由视口改为该祖先。说的简单点,就是position:fixed
的元素会相对于最近的并且应用了transform的祖先元素定位,而不是窗口。导致这个现象的原因是使用了transform的元素将创建一个新的堆叠上下文。堆叠上下文(Stacking Context):堆叠上下文是 HTML 元素的三维概念,这些 HTML 元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的 z 轴上延伸,HTML 元素依据其自身属性按照优先级顺序占用层叠上下文的空间。顺序如下图所示,总之堆叠上下文会对定位关系产生影响。想要进一步可以查看不受控制的 position:fixed。
键盘弹出与使用transform属性的情况在移动端是很常见的,所以需要谨慎使用position:fixed
。
推荐使用flex
flex,即弹性布局,移动端兼容性较好,能够满足大部分布局需求。现在我们使用flex来实现h5中常见的顶部标题栏+中部滚动内容+底部导航栏的布局;实现效果如下:
首先我们来实现整体的布局,整体布局应该是一个方向为flex-direction: column;
并且占据整个窗口的弹性盒子,然后里面的布局,应该是首尾为固定高度,中间内容区域为flex: 1;
。
html, body { padding: 0; margin: 0; } .page { position: absolute; top: 0; bottom: 0; width: 100%; display: flex; flex-direction: column; .page-content { flex: 1; overflow-y: auto; } }
然后再来实现底部标题栏,底部标题栏一般由居中标题和左右操作区域组成;为了实现中间区域标题居中,我们左右两部分应该保持相同的宽度。
<template> <div> <div> 左 </div> <div> <slot>Title</slot> </div> <div> <div>右</div> </div> </div> </template> <script> export default { name: 'HeadBar' } </script> <style scoped> .header { display: flex; width: 100%; line-height: 88px; height: 88px; font-size: 36px; background-color: #42b983; z-index: 999; color: #fff; transition: background-color .5s ease; .header__main { flex: 1; display: flex; justify-content: center; align-items: center; text-align: center; } .header__left, .header__right { padding: 0 16px; width: 120px; } .header__left { text-align: left; } .header__right { text-align: right; } } </style>
底部导航栏主体部分,其实是单个导航选项平分导航栏;而每个导航选项,是一个方向为flex-direction: column;
布局方式横向为align-items: center;
,竖向为justify-content: space-around;
<template> <div> <div> <div></div> <span>选项1</span> </div> <div> <div></div> <span>选项2</span> </div> <div> <div></div> <span>选项3</span> </div> <div> <div></div> <span>选项4</span> </div> </div> </template> <script> export default { name: 'BottomTaber', data () { return { } } } </script> <style scoped> .taber { background-color: #42b983; color: #fff; height: 88px; display: flex; .taber-item { flex: 1; display: flex; flex-direction: column; justify-content: space-around; align-items: center; } .icon { width: 36px; height: 36px; background-color: #fff; } } </style>
页面跳转
转场动画
在vue中我们通过vue-router来管理路由,每个路由跳转类似与在不同的页面之间进行切换,从用户友好的角度来说,每次切换页面的时候最好添加一个转场效果。如果转场动画不区分路由是打开新页面、还是返回之前页面我们只需要在<router-view>
外使用<transition>
添加一个动画效果即可;但是一般打开和返回是应用不同的动画效果的,所以我们需要在切换路由的时候区分路由是前进还是后退。为了区分路由的动作,我们在路由文件中设置meta为数字,meta表示其路由的深度,然后监听$route,根据to、from meta值的大小设置不同的跳转动画。如果应用到多种跳转动画,可以根据详情,具体情况具体应用。
<template> <transition :name="transitionName"> <router-view></router-view> </transition> </template> <script> export default { name: 'app', data () { return { transitionName: 'fade' } }, watch: { '$route' (to, from) { let toDepth = to.meta let fromDepth = from.meta if (fromDepth > toDepth) { this.transitionName = 'fade-left' } else if (fromDepth < toDepth) { this.transitionName = 'fade-right' } else { this.transitionName = 'fade' } } } } </script>
虽然这样能够实现跳转效果,但是需要在编写router时添加设置,比较麻烦;我们可以使用开源项目vue-navigation
来实现,更加方便,无须对router进行多余的设置。npm i -S vue-navigation
安装,在main.js中导入:
import Navigation from 'vue-navigation' Vue.use(Navigation, {router}) // router为路由文件
在App.vue中设置:
this.$navigation.on('forward', (to, from) => { this.transitionName = 'fade-right' }) this.$navigation.on('back', (to, from) => { this.transitionName = 'fade-left' }) this.$navigation.on('replace', (to, from) => { this.transitionName = 'fade' })
vue-navigation插件还有一个重要的功能就是保存页面状态,与keep-alive相似,但是keep-alive保存状态无法识别路由的前进后退,而实际应用中,我们的需求是返回页面时,希望页面状态保存,当进入页面时希望获取新的数据,使用vue-navigation可以很好的实现这个效果。具体使用可以查看vue-navigation有详细使用说明与案例。另外也可以尝试vue-page-stack,两个项目都能实现我们需要的效果,vue-page-stack
借鉴了vue-navigation
,也实现了更多的功能,并且最近也一直在更新。
PS: 这里的动画效果引用自animate.scss;
底部导航栏
之前我们已经实现了底部导航栏的基本样式,这里我们再做一些说明。当页面路由路径与router-link
的路由匹配时,router-link
将会被设置为激活状态,我们可以通过设置active-class
来设置路径激活时应用的类名,默认为router-link-active
,而激活的类名还有一个router-link-exact-active
,这个类名是由exact-active-class
来设置的,同样是设置路径激活时应用的类名;active-class
与exact-active-class
其实是由路由的匹配方式决定的。
一般路由的匹配方式是包含匹配。 举个例子,如果当前的路径是 /a 开头的,那么 也会被设置 CSS 类名。按照这个规则,每个路由都会激活 ,而使用exact
属性可以使用“精确匹配模式”。精确匹配只有当路由完全相同的时候才会被激活。
路由守卫
移动端的路由守卫一般不会太复杂,主要是登录权限的判断,我们设置一个路由白名单,将所有不需要登录权限的路由放入其中;对于需要登录的路由做判断,没有登录就跳转登录页面,要求用户进行登录后在访问,如果登录后需要返回原有路由就把目标页面的路由作为参数传递给登录页面,再在登录后进行判断,如果存在目标页面参数就跳转目标页面,没有就跳转首页。
如果你的应用涉及到权限,那需要标注每个路由需要的权限,在meta中设置roles,roles是数组来保存需要的权限;从后台的接口中获取用户拥有的权限和roles进行对比就可以判断是否具有相关权限了。
const whiteList = ['/login'] router.beforeEach((to, from, next) => { const hasToken = store.getters.auth if (hasToken) { if (to.path === '/login') { next({ path: '/' }) } else { const needRoles = to.meta && to.meta.roles && to.meta.roles.length > 0 if (needRoles) { const hasRoles = store.state.user.roles.some(role => to.meta.roles.includes(role)) if (hasRoles) { next() } else { next('/403') } } else { next() } } } else { if (whiteList.includes(to.path)) { next() } else { next('/login') } } })
组件
自动加载
在我们的项目中,往往会使用的许多组件,一般使用频率比较高的组件为了避免重复导入的繁琐一般是作为全局组件在项目中使用的。而注册全局组件我们首先需要引入组件,然后使用Vue.component
进行注册;这是一个重复的工作,我们每次创建组件都会进行,如果我们的项目是使用webpack构建(vue-cli也是使用webpack),我们就可以通过require.context
自动将组件注册到全局。创建components/index.js
文件:
export default function registerComponent (Vue) { /** * 参数说明: * 1. 其组件目录的相对路径 * 2. 是否查询其子目录 * 3. 匹配基础组件文件名的正则表达式 **/ const modules = require.context('./', false, /\w+.vue$/) modules.keys().forEach(fileName => { // 获取组件配置 const component = modules(fileName) // 获取组件名称,去除文件名开头的 `./` 和结尾的扩展名 const name = fileName.replace(/^\.\/(.*)\.\w+$/, '$1') // 注册全局组件 // 如果这个组件选项是通过 `export default` 导出的, // 那么就会优先使用 `.default`, // 否则回退到使用模块的根。 Vue.component(name, component.default || component) }) }
之后在main.js
中导入注册模块进行注册,使用require.context
我们也可以实现vue插件和全局filter的导入。
import registerComponent from './components' registerComponent(Vue)
通过v-model绑定数据
v-model
是语法糖,它的本质是对组件事件进行监听和数据进行更新,是props和$on监听事件的缩写,v-model
默认传递value
,监听input
事件。现在我们使用v-model
来实现下数字输入框,这个输入框只能输入数字,在组件中我们只需要定义value来接受传值,然后在输入值满足我们输入条件(输入为数字)的时候使用$emit
触发input
事件。
<template> <div> <input type="text" :value="value" @input="onInput"> </div> </template> <script> export default { name: 'NumberInput', props: { value: String }, methods: { onInput (event) { if (/^\d+$/.test(event.target.value)) { this.$emit('input', event.target.value) } else { event.target.value = this.value } } } } </script>
使用的时候,我们只需要使用v-model
绑定值就可以了。v-model
默认会利用名为value
的prop
和名为input
的事件,但是很多时候我们想使用不同的prop
和监听不同的事件,我们可以使用model
选项进行修改。
Vue.component('my-checkbox', { model: { prop: 'checked', event: 'change' }, props: { // this allows using the `value` prop for a different purpose value: String, // use `checked` as the prop which take the place of `value` checked: { type: Number, default: 0 } }, // ... })
<my-checkbox v-model="foo" value="some value"></my-checkbox>
上述代码相当于:
<my-checkbox :checked="foo" @change="val => { foo = val }" value="some value"> </my-checkbox>
通过插件的方式来使用组件
在很多第三方组件库中,我们经常看到直接使用插件的方式调用组件的方式,比如VantUI的Dialog弹出框组件,我们不但可以使用组件的方式进行使用,也可以通过插件的形式进行调用。
this.$dialog.alert({ message: '弹窗内容' });
将组件作为插件使用的原理其实并不复杂,就是使用手动挂载Vue组件实例。
import Vue from 'vue'; export default function create(Component, props) { // 先创建实例 const vm = new Vue({ render(h) { // h就是createElement,它返回VNode return h(Component, {props}) } }).$mount(); // 手动挂载 document.body.appendChild(vm.$el); // 销毁方法 const comp = vm.$children[0]; comp.remove = function() { document.body.removeChild(vm.$el); vm.$destroy(); } return comp; }
调用create
传入组件和props
参数就可以获取组件的实例,通过组件实例我们就可以调用组件的各种功能了。
<template> <div v-show="visible"> 加载中 </div> </template> <script> export default { name: 'Loading', data () { return { visible: false } }, methods: { show () { this.visible = true }, hide () { this.visible = false } } } </script> <style scoped> .loading-wrapper { position: absolute; top: 0; bottom: 0; width: 100%; background-color: rgba(0, 0, 0, .4); z-index: 999; } </style> <!--使用--> const loading = create(Loading, {}) loading.show() // 显示 loading.hide() // 关闭
第三方组件
移动端各种组件、插件已经相对完善,在项目开发中重复造轮子是一件很不明智的事情;开发项目时我们可以借助第三方组件、插件提高我们的开发效率。
常用组件库
VantUI是有赞开源的一套轻量、可靠的移动端Vue组件库;支持按需引入、主题定制、SSR,除了常用组件外,针对电商场景还有专门的业务组件,如果是开发电商项目的话,推荐使用。官方文档关于主题定制是在webpack.config.js
中进行设置的:
// webpack.config.js module.exports = { rules: [ { test: /\.less$/, use: [ // ...其他 loader 配置 { loader: 'less-loader', options: { modifyVars: { // 直接覆盖变量 'text-color': '#111', 'border-color': '#eee' // 或者可以通过 less 文件覆盖(文件路径为绝对路径) 'hack': `true; @import "your-less-file-path.less";` } } } ] } ] };
但我们的项目可能是使用vue-cli构建,这时我们需要在vue.config.js
中进行设置:
module.exports = { css: { loaderOptions: { less: { modifyVars: { 'hack': `true; @import "~@/assets/less/vars.less";` } } } } }
常用插件
better-scroll是一个为移动端各种滚动场景提供丝滑的滚动效果的插件,如果在vue中使用可以参考作者的文章当 better-scroll 遇见 Vue。
swiper是一个轮播图插件,如果是在vue中使用可以直接使用vue-awesome-swiper,vue-awesome-swiper
基于Swiper4
,并且支持SSR。
【관련 권장사항: vuejs 동영상 튜토리얼, 웹 프론트엔드 개발】
위 내용은 모바일 웹 개발에서 Vue를 사용할 수 있나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!