Git Product home page Git Product logo

blog's People

Contributors

xiqe avatar

Stargazers

 avatar

Watchers

 avatar  avatar

blog's Issues

前端响应式布局解决方案

前端响应式布局解决方案

移动端页面布局的问题,在当今前端是比较常见的话题,因为不同终端设备的分辨率,dpr等不同,导致前端无法用单一的px值来将页面的元素固定,需要通过技术手段在不同的环境下进行适配,来保证页面最佳的可读性,这里将会总结一些常见的方案。

百分比布局

这是最早的布局解决方案,解决成本低,高效。

将页面元素的宽度以百分比来进行设置,高度不变。

缺点也显而易见,在例如ipad横屏下,页面被拉伸很多,造成视觉体验较差。

Alt text

Media 媒体查询

为了解决百分比布局带来的宽屏视觉上的缺点,引入了media的方案,将设备按分辨率分成几个标准化的区间,视觉根据区间设计出2-3个方案(方案数量决定了精细化的程度),前端根据media来区分在某个分辨率区间读取相应的css方案。

缺点是需要设计和前端同时多出多个方案。

Alt text

代码模式:

html{font-size:10px}
@media screen and (min-width:320px) and (max-width:480px){html{font-size:12px}}
@media screen and (min-width:480px) and (max-width:640px){html{font-size:14px}}
@media screen and (min-width:640px){html{font-size:16px}}

rem布局(主流解决方案Flexible)

根据标准视觉稿,先作出一套以rem为单位的页面,然后通过在不同分辨率的情况下,用js或media去改变根html的font-size的值,从而到达页面的自适应效果。

Flexible作为比较知名的成熟方案,也已经出现很久了,技术原理主要就是为了能让页面更好的适配各种不同的终端,通过Hack手段来根据设备的dpr值相应改变标签中viewport的值,从而让页面达么缩放的效果,也变相的实现页面的适配功能。

<!-- dpr = 1--> <meta name="viewport" content="initial-scale=scale,maximum-scale=scale,minimum-scale=scale,user-scalable=no">
<!-- dpr = 2--> <meta name="viewport" content="initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5,user-scalable=no"> 
<!-- dpr = 3--> <meta name="viewport" content="initial-scale=0.3333333333,maximum-scale=0.3333333333,minimum-scale=0.3333333333,user-scalable=no">

Flexible技术原理:

  • 根据dpr的值来修改viewport实现1px的线

  • 根据dpr的值来修改html的font-size,从而使用rem实现等比缩

  • 使用Hack手段用rem模拟vw特性

(效果图见Viewport)

Viewport

Viewport相关的单位有四个,分别为vw、vh、vmin和vmax。

vw:是Viewport's width的简写,1vw等于window.innerWidth的1%。

vh:和vw类似,是Viewport's height的简写,1vh等于window.innerHeihgt的1%。

vmin:vmin的值是当前vw和vh中较小的值。

vmax:vmax的值是当前vw和vh中较大的值。

在这个方案中,我们用vw来代替rem的缩放方案,就可以将页面做到任意环境下的同等自适应缩放。

Alt text

总结

最后想要说的是,以上几个方案并非唯一绝对的方案,而是可以根据自身业务产品需求,进行有效组合取舍,找到适合自己项目的才是真正的最佳实践。

本文Demo使用了Viewport的方式来进行布局,并且通过media来实现web端在2k,4k物理像素屏(例如大屏电视)下的自适应兼容,利用高度来区分电脑屏和电视屏(高度仅测试,无实际意义)。为实现一套代码多端兼容的简单Demo。

Vue开发实战笔记 -- 基础篇

基础理论

属性

自定义属性:组件props中声明的属性
原生属性:没有声明的属性,默认自动挂载到组件根元素上,设置inheritAttrs为false可关闭自动挂载
特殊属性:挂载到组件根元素上,支持字符串、对象、数组等多种语法

事件

普通事件

@click @input @change @xxx等事件
this.$emit('eventName'...)出发

修饰符事件

@input.trim @click.stop @submit.prevent 等
一般用于原生HTML元素,自定义组件需要自行开发支持

插槽

普通插槽

<template slot="xxx">...</template>
<template v-slot:xxx>...</template>

作用域插槽

<template slot="xxx" slot-scope="props">...</template>
<template v-slot:xxx="props">...</template>

属性、事件、插槽完全都可以通过属性解决,大属性的概念

双向绑定与单项数据流

双向绑定:model与view互相更新
单项数据流:model更新view

  • Vue是单项数据流,非双向绑定
  • Vue的双向绑定不过是语法糖
  • Object.defineProperty是用来做响应式更新的,和双向绑定没关系

如何实现双向绑定(语法糖)

<PersonalInfo 
    v-model="phoneInfo"
    :zip-code.sync="zipCode"
/>

<PersonalInfo 
    :phone-info="phoneInfo"
    @change="val=>(phoneInfo = val)"
    :zip-code.sync="zipCode"
    @update:zipCode="val=>(zipCode = val)"
/>

虚拟Dom与Key属性

JQuery:Event ===> Element
Vue:Event ====> State ====> Element

Virtual DOM

Alt text

Virtual DOM Diff(对比同层节点比较):

Alt text

如果节点类型不同,直接干掉前面的节点,再创建并插入新的节点,不会再比较这个节点以后的子节点了。
如果节点类型相同,则会重新设置该节点的属性,从而实现节点的更新。
  • 场景1:移动
    Alt text
  • 场景2:删除后新建
    Alt text
  • 场景3:删除后新建
    Alt text
  • 场景4:更新删除新建(无Key)
    Alt text
  • 场景5:移动(有Key)
    Alt text
  • 场景6:插入(有Key)
    Alt text

组件的数据来源分类

  • 来自父元素的属性
  • 来自组件自身的状态
  • 来自状态管理器,如vuex,Vue.observable

计算属性及侦听器

计算属性 computed

  • 减少模版中计算逻辑
  • 数据缓存
  • 依赖固定的数据类型(响应式)

侦听器 watch

  • 更加灵活,通用
  • watch中可以执行任何逻辑,如函数节流、Ajax异步获取数据,甚至操作DOM

computed VS watch

  • computed能做的,watch都能做,反之则不行
  • 能用computed的尽量用computed

函数式组件

  • functional:true
  • 无状态、无实例、没有this上下文、无生命周期
export default {
    name: 'functional-button',
    functional: true,
    render(createElement, context) {
        return createElement('button', 'click me')
    }
}

<template>
    <FunctionalButton>
        click me
    </FunctionButton>
</template>

Vue自定义指令

生命周期

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

案例

Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

<input v-focus>

template和JSX的对比以及它们的本质

template

  • 模版语法(HTML的扩展)
  • 数据绑定使用Mustache语法(双大括号)
  • 优点:学习成本低、大量内置指令简化开发、组件作用域CSS
  • 缺点:灵活性低
<span>Message:{{ msg }}</span>

JSX

  • Javascript 的语法扩展
  • 数据绑定使用单引号
  • 灵活灵活灵活
<span>Message:{ this.msg }</span>

template & JSX都是语法糖

Q&A

  1. 子组件为何不可以修改父组件传递的Prop,如果修改了,Vue是如何监控到属性的修改并给出警告的?
Vue是单项数据流的。Object.defineProperty()
  1. this.$emit的返回值是什么?
返回值是 this
如果需要需要返回值可以使用回调参数
  1. 相同名称的插槽是合并还是替换
替换。
  1. 为什么不能用index作为key?
更新DOM性能问题
会引入状态bug问题(因为array改变后,index会动态的相应改变)
  1. 数组有哪些方式支持响应式更新,如不支持如何处理,底层原理如何实现的?
- 支持:push、pop、shift、unshift、splice、sort、reverse
- 不支持:filter、concat、slice
- 原理同样使用Object.defineProperty对数组方法进行改写
  1. 进行节流改造,即直到用户停止输入超过500毫秒后才更新 fullName
watch:{
    firstName:function(val){
        clearTimeout(this.firstTimeout);
        this.firstTimeout = setTimeout(()=>{
            this.fullName = val + '"" + this.lastName
        },500)
    }
}

只能用watch,无法使用computed

*该笔记基于 Vue开发实战课程 整理

【研究与总结】前端请求的特性区分表

XHR $.ajax fetch axios
是否底层API - -
实现 - XHR - XHR
Promise - -
浏览器兼容 ActiveXObject polyfill polyfill
适用 MVC MVC MVVM MVVM
Status Code - -
Jsonp - - -
拦截器 - -
abort -
timeout -
progress -
node - - -
引入成本 - -

Vue开发实战笔记 -- 进阶篇

进阶生态

在Vue中使用vuex

//main.js
const store = new Vuex.Store({
	state:{
		count:0
	},
	mutations:{
		INCREMENT(state){
			state.count++
		}
	},
	actions:{
		increment(commit){
		      setTimeout(() => {
		         commit('INCREMENT')
		    }, 1000)
		}
	},
	getters:{
		doubleCount(state){
			return state.count+2
		}
	}
})

//App.vue
<template>
	<div id="app">
		{{count}}
		<br />
		{{$store.getters.doubleCount}
		<button @click="$store.commit('INCREMENT')">count++</button>
		<button @click="$store.dispatch('increment')">count++</button>
	</div>
</template>

...
computed:{
	count(){
		return this.$store.state.count
	}
}
...

vuex核心概念及底层原理

  • State:state类似与组件中的data数据,而data只仅局部使用于该组件中,state数据是全局的可以再任何一个组件使用,并且我们可以在单个组件中修改state的数据。
  • Mutations:mutations 类似于组件里面的methods在mutations里面可以对state的数据进行修改。mutations必须是同步的。同步的意义在于这样每一个 mutation 执行完成后都可以对应到一个新的状态(和 reducer 一样),这样 devtools 就可以打个 snapshot 存下来。
  • Actions:actions 只是一个架构性的概念,并不是必须的,说到底只是一个函数。区分 actions 和 mutations 并不是为了解决竞态问题,而是为了能用 devtools 追踪状态变化。actions可以包含任意异步操作。
  • Getters: getters存放公共函数供组件调用,类似于组件中的过滤函数computed或者filters,缓存state的数据。
  • Module:Vue.set动态添加state到响应式数据中

实现原理(响应式数据)

import Vue from 'vue'
const Store = function Store (options={}){
	const {state={},mutations={}} = options;
	this._vm = new Vue({          // 将state放在Vue的data下,就变成了响应式的数据
		data:{
			$$state: state
		}
	})
	this._mutations = mutations
}
Store.prototype.commit = function(type,payload){
	if(this._mutations[type]){
		this._mutations[type](this.state,payload)
	}
}
Object.defineProperties(Store.prototype,{      // 将state的get重写,每次get值的时其实是访问_vm实例下state 的值
	state:{
		get:function(){
			return this._vm._data.$$state
		}
	}
})

vuex辅助函数

  • State ---- this.$store.state.xxx ---- mapState 取值
  • Getter ---- this.$store.getters.xxx ---- mapGetters 取值
  • Mutation ---- this.$store.commit('xxx') ---- mapMutations 取值
  • Action ---- this.$store.dispatch('xxx') ---- mapActions 取值
computed: mapState({
	products: state => state.products
})
// 等价于
computed: {
	products(){
		return this.$store.state.products
	}
}

methods:mapAction('cart',{
	'addProductToCart'
})
// 等价于
methods;{
	addProductToCart(product){
		this.$store.dispatch('cart/addProductToCart',product)
	}
}

Vue-Router的使用场景

解决的问题

  • 监听URL的变化,并在变化前后执行相应的逻辑
  • 不同的URL对应不同的组件
  • 提供多种方式改变URL的API(URL的改变不能导致浏览器刷新)

路由类型以及底层原理

类型

  • hash模式:丑,无法使用锚点定位
  • history模式:需要后端配合,IE9不兼容(可使用强制刷新处理)

底层原理

Alt text

Nuxt

SPA模式缺点

  • 不利于SEO 服务端渲染 SSR
  • 首屏渲染时间长 预渲染 Prerendering

Nuxt的作用

  • 静态站点
  • 动态渲染
  • 简化配置

SSR流程图

Alt text

单元测试的使用

使用方式

  • jest or mocha
  • @vue/test-utils
  • sinon

*该笔记基于 Vue开发实战课程 整理

【研究与总结】关于前端请求的那些事(xhr/ajax/fetch/axios)

引言

前端是一个快速发展的领域,而在前端的技术栈当中,前端请求又是最见的一个领域,通过请求接口数据,才能将一个静态的页面动态化。本文将以前端发展的时间轴来逐一分析前端请求的技术演变及其优劣,针对这一课题,作者查阅了相关资料加以自己的理解,如有错误,烦请指出。

XMLHttpRequest

XMLHttpRequest是最早出现的与服务器交换数据的方案,有了XMLHttpRequest,开发者终于可以在不重新加载页面的情况下更新网页,可以在页面加载后请求接受以及发送数据。而所有浏览器均可以获取XMLHttpRequest对象:

var xhr = new XMLHttpRequest(); //获取xhr对象

但是XMLHttpRequest是个比较粗燥的底层对象,各个浏览器对其的创建方法也不同,以下是兼容方法:

var xhr;
if (window.XMLHttpRequest) { // Mozilla, Safari...
  xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
  try {
    xhr = new ActiveXObject('Msxml2.XMLHTTP');
  } catch (e) {
    try {
      xhr = new ActiveXObject('Microsoft.XMLHTTP');  //IE5,6
    } catch (e) {}
  }
}

使用XMLHttpRequest发起一个get请求

//get请求
xhr.open("GET","test1.txt",true);
xhr.send();

完整的post请求代码如下:

var xhr;
if (window.XMLHttpRequest) { // Mozilla, Safari...
  xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
  try {
    xhr = new ActiveXObject('Msxml2.XMLHTTP');
  } catch (e) {
    try {
      xhr = new ActiveXObject('Microsoft.XMLHTTP');
    } catch (e) {}
  }
}
if (xhr) {
  xhr.onreadystatechange = onReadyStateChange;
  xhr.open('POST', '/api', true);
  // 设置 Content-Type 为 application/x-www-form-urlencoded
  // 以表单的形式传递数据
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.send('username=admin&password=root');
}


// onreadystatechange 方法
function onReadyStateChange() {
  // 该函数会被调用四次
  if (xhr.readyState === 4 &&xhr.status === 200) {
    console.log('执行成功');
  } else {
    console.log('执行出错');
  }
}

Jquery Ajax

说到Jquery,这是一个时代,几乎**了前端10年有余,彻底解决了UI层与数据层交互的问题,直到三大框架(Angular/React/Vue)的出现,前端进入MVVM浪潮。而Ajax将XHR进行封装,让开发者可以更加便捷方便进行使用。

$.ajax({   //标准写法
   type: 'POST',
   url: url,
   data: data,
   dataType: dataType,
   success: function () {},
   error: function () {}
});
$.get(url,function(){}); //get请求
$.post(url,body,function(){}); //post请求
$.getJSON(url,function(){});  //get请求从服务器加载Json编码

优点:

  • 对原生XHR的封装
  • 针对MVC的编程
  • 完美的兼容性
  • 支持jsonp

缺点:

  • 不符合MVVM
  • 异步模型不够现代,不支持链式,代码可读性差
  • 整个Jquery太大,引入成本过高

Fetch

fetch其实是一个新世界,脱离的XHR,完全是基于Promise的异步处理机制,使用起来会比起ajax更加简单。

使用fetch的代码会相比xhr来说更具有条理性

fetch(url).then(function(response) {
  return response.json();
}).then(function(data) {
  console.log(data);
}).catch(function(e) {
  console.log("Oops, error");
});

在使用ES6的箭头函数后

fetch(url).then(response => response.json())
  .then(data => console.log(data))
  .catch(e => console.log("Oops, error", e))

优点:

  • 更加底层,提供的API丰富(request, response)
  • 语法简单,脱离了XHR,基于ES新的Promise设计

看到以上,或许你会觉得fetch真的很美好,但是请了解,fetch本身是一个 low-level 的 API,它注定不会像你习惯的 $.ajax 或是 axios 等库帮你封装各种各样的功能或实现。

所以它是存在一定的缺点:

  • 兼容性比较凄惨,低级别浏览器均不支持,需要实现fetch的polyfill了。思路其实很简单,就是判断浏览器是否支持原生的fetch,不支持的话,就仍然使用XMLHttpRequest的方式实现,同时结合Promise来进行封装。常见的polyfill就有:es6-promise,babel-polyfill,fetch-ie8等

Alt text

  • 不支持jsonp,可以引入fetch-jsonp
//安装
npm install fetch-jsonp --save-dev

//使用
fetchJsonp(url, {
  timeout: 3000,
  jsonpCallback: 'callback'
}).then(function(response) {
  console.log(response.json());
}).catch(function(e) {
  console.log(e)
});
  • 没有拦截器,需要额外再封装一层或者fetch-interceptor
  • 默认不带cookie,需要添加配置
fetch(url,{
  credentials: 'include' //include表示cookie既可同域,也可跨域,‘same-origin’表示只可同域
});
  • 没有abort,不支持timeout超时处理

可以用Promise.race()实现,Promise.race(iterable) 方法返回一个Promise对象,只要 iterable 中任意一个Promise 被 resolve 或者 reject 后,外部的Promise 就会以相同的值被 resolve 或者 reject。

  • 无法获取progress状态

fetch中的Response.body 中实现了getReader()方法用于读取原始字节流, 该字节流可以循环读取。参考javascript - Progress indicators for fetch? - Stack Overflow
2016 - the year of web streams

Axios

axios也是比较新的网络请求的类库,并且被尤雨溪尤大推荐,已成为VUE的网络请求标配,也是十分的火爆。它本身也是对原生XHR的封装。

  • 支持node,创建http请求
  • 支持Promise API
  • 客户端防止CSRF:每个请求带一个cookie拿到的key
  • 拦截请求和响应
  • 可取消请求

兼容性上虽然axios本质上也是对原生XHR的封装,但是它也依赖原生ES6 Promise的实现,和fetch一样需要polyfill的兼容。

Alt text

安装

//npm
npm install axios

//cdn
<script src="https://unpkg.com/axios/dist/axios.min.js"><\/script>

基本使用如下:

axios({
    method: 'GET',
    url: url,
})
.then(res => {console.log(res)})
.catch(err => {console.log(err)})

// get请求
axios.get(url)
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

// post请求
axios.post(‘/user’, {
    name: 'Jerry',
    lastName: 'Liang'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

特殊场景的处理

在开发过程中,经常会遇到比较尴尬的场景就是多请求的串行与并发,并发比较容易解决,不存在回调地狱,但是代码可读性就会容易变得很渣,而串行问题对于前端是绝望的,最好的办法是后端来做合并,如果后端不做这块的处理,前端就必须来面对回调地狱。

多请求串行

// ajax
$.ajax({
    url: '',
    data: '',
    success: function (data) {
       $.ajax({
            url: '',
            data: '',
            success: function (data) {
                $.ajax({
                    // 如此一层嵌套一层
                })
            }
        }) 
    }
})

//axios
axios.get(url)
.then(res => {
    return axios.get(url,{
         {name:result.name}
    });
}).then(res => {
    //如此一层层嵌套
});

多请求并行

//ajax 通过计数器实现(虽然Jquery支持$.when的方式,但此处不做案例)
var num = 0;
function all(){
    num++;
    if(n>=3)console.log('三个请求全部完成')
}
$.ajax({
    url: '',
    data: '',
    success: function (data) {
       console.log("ajax请求1 完成");
       all();
    }
})
$.ajax({
    url: '',
    data: '',
    success: function (data) {
       console.log("ajax请求2 完成");
       all();
    }
})
$.ajax({
    url: '',
    data: '',
    success: function (data) {
       console.log("ajax请求3 完成");
       all();
    }
})

//axios
function getInfo() {
  return axios.get(url);
}
function getUser() {
  return axios.get(url);
}
axios.all([getInfo(), getUser()])
  .then(axios.spread(function (info, user) {
    // 两个请求现在都执行完成
  }));

如何选择(个人理解,仅供参考)

  1. 首先可以肯定的是,如果你的代码依旧是基于Jquery,那毫无疑问,ajax就是你最好的选择。
  2. 如果你使用的是任意MVVM框架,建议无脑使用axios,fetch在实际项目使用中,需要各种的封装和异常处理,并非开箱即用,而axios可以做到直接替换$.ajax。
  3. 如果就是要使用fetch,那相信你也一定能封装成自己的一套最佳实践。

附录:特性区分表

XHR $.ajax fetch axios
是否底层API - -
实现 - XHR - XHR
Promise - -
浏览器兼容 ActiveXObject polyfill polyfill
适用 MVC MVC MVVM MVVM
Status Code - -
Jsonp - - -
拦截器 - -
abort -
timeout -
progress -
node - - -
引入成本 - -

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.