微信小程序跳转方式
1.navigator 跳转
最常见的跳转方法就是运用<navigator url="../../.."></navigator>进行跳转,只要在url中添加跳转页面的路径即可。
代码
<navigator url="../skill/skill">
........//跳转涵盖内部所有代码形成的页面
</navigator>
1
2
3
2.运用 bindtap在js中实现页面的跳转
比起navigator的跳转,个人更喜欢用bindtap跳转,运用更灵活,也不用去除点击样式。
bindtap='home'(home位置随便替换)
代码
//wxml
<view bindtap='home'>
</view>
//js
home: function (e) {
wx.navigateTo({
url: '../../..' //跳转链接
})
}
**重点
在开发的过程中,突然遇到以上两种方式都无法实现跳转,也不会报错的情况,经过查询资料,发现内部调用wx.switchTab可以很好的解决这一现象
代码
home: function (e) {
wx.switchTab({
url: '../../..'
})
}
一、前言
我们都知道,vue组件中通信是用props进行父子通信,或者用provide和inject注入的方法,后者类似与redux的porvider,父组件使用,包含在里面的子组件都可以使用,provide/inject用法看我的博客(provide/inject用法),provide/indect官方文档,不过provide/indect一般用的不多,都是用前者,但是props有一个问题,父传子没问题,但是子后面还有子,子后面还有子,子子孙孙无穷尽也,所以这就要一层层的传下去,太麻烦了,所以vuex就派上用场了,vuex作为一个很轻量的状态管理器,有着简单易用的的API接口,在任意组件里面都能使用,获取全局状态,统一获取改变,今天,就看一下源码怎么实现的。
二、准备
1)先去github上将vuex源码下载下来。
2)打开项目,找到examples目录,在终端,cd进入examples,执行npm i,安装完成之后,执行node server.js
3)执行上述之后,会得到下方的结果,表示编译完成,打开浏览器 输入 localhost:8080
4) 得到页面,点击counter
5)最终,我们就从这里出发调试吧。
三、调试
找到counter目录下的store.js 写一个debugger,浏览器打开F12,刷新页面,调试开始。
1.Vue.use()
先进入Vue.use(Vuex),这是Vue使用插件时的用法,官方文档也说了,Vue.use,会调用插件的install方法,所以如果你要写插件的话,你就要暴露一个install方法,详情请看vue官方文档之use的用法
即use会调用下方的方法(debugger会进入)
function initUse (Vue) {
Vue.use = function (plugin) {
var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// additional parameters
var args = toArray(arguments, 1);
args.unshift(this);
if (typeof plugin.install === 'function') {
// 如果插件的install是一个function,调用install,将 this指向插件,并将Vue作为第一个参数传入
// 所以调用vuex吧this指向vuex,并吧vue当参数传入
plugin.install.apply(plugin, args);
} else if (typeof plugin === 'function') {
plugin.apply(null, args);
}
installedPlugins.push(plugin);
return this
};
}
1.1 vuex的install方法
在源码目录的src目录下的store.js文件里最下方有个install函数,会调用它
debugger进入后
export function install (_Vue) { // _Vue是上面说的Vue作为第一个参数 ,指针this指向Vuex
if (Vue && _Vue === Vue) {
// 如果你在开发环节,你使用了两次Vue.use(Vuex) 就会报下方的错误,提醒你vue只能被use一次,可以自行试试
if (process.env.NODE_ENV !== 'production') {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
Vue = _Vue
applyMixin(Vue) // 调用applyMixin方法
}
1.2 vuex的applyMixin方法
applyMixin方法在mixin.js文件里 同样在src目录下
export default function (Vue) {
const version = Number(Vue.version.split('.')[0]) // 获取vue的版本
if (version >= 2) { // vue的版本是2.xx以上 执行vue的mixin混入,该函数不懂用法请查看官方文档,
// mixin合并混入操作,将vuexInit 方法混入到beforeCreate生命周期,意思就是当执行beforeCreate周期的时候
// 会执行vuexInit 方法
Vue.mixin({ beforeCreate: vuexInit })
} else { // 1.xxx版本太老了 现在基本不用,暂不讲解,可以自行了解
// override init and inject vuex init procedure
// for 1.x backwards compatibility.
const _init = Vue.prototype._init
Vue.prototype._init = function (options = {}) {
options.init = options.init
? [vuexInit].concat(options.init)
: vuexInit
_init.call(this, options)
}
}
/*
Vuex init hook, injected into each instances init hooks list.
/
function vuexInit () {
// 因为该方法是在beforeCreate下执行,而beforeCreate的this指向为Vue 所以this === Vue
// 获得vue里面的option配置,这里涉及到vue的源码,以后再讲vue ,
//所以这就是我们为什么要在new Vue的时候,传递一个store进去的原因,
//因为只有传进去,才能在options中获取到store
/
var vm = new Vue({
el: "#app",
data() {return{}},
store
})
*/
const options = this.$options
// store injection
if (options.store) {
// 如果options对象中store有,代表是root ,如果options.store是函数,执行调用options.store()
// 如果是对象直接options.store 赋值给this.$stroe
// 这也就是我们为什么能够在Vue项目中直接this.$store.disptach('xxx')的原因,从这里获取
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
// 如果options.store为空,则判断options.parent.$store有没有 从父元素判断有没有store,
//从而保证子元素父元素公用一个store实例
this.$store = options.parent.$store
}
}
}
1.3 Vue.use(Vuex)总结
至此,Vue.use(Vuex)全部分析完成,总结,就是Vue.use调用Vuex的install的方法,然后install使用mixin混入beforecreate生命周期中混入一个函数,当执行生命周期beforecreate的时候回执行vuexInit 。你可以慢慢调试,所以要好好利用下方的调试按钮,第二个按钮执行下一步,第三个进入方法,两个配合使用。
2.new Vuex.Store
Vue.use(Vuex)已经结束,再回到counter目录下的store.js文件
export default new Vuex.Store({
state,
getters,
actions,
mutations
})
debugger进入,Vuex.Store方法在src目录下的store.js文件下
export class Store {
constructor (options = {}) {
// 这里的options是在counter定义的 state,getters,actions,mutations当做参数传进来
// Auto install if it is not done yet and window
has Vue
.
// To allow users to avoid auto-installation in some cases,
// this code should be placed here. See #731
if (!Vue && typeof window !== 'undefined' && window.Vue) {
// 挂载在window上的自动安装,也就是通过script标签引入时不需要手动调用Vue.use(Vuex)
install(window.Vue)
}
if (process.env.NODE_ENV !== 'production') {
// 开发环境 断言,如果不符合条件 会警告,这里自行看
assert(Vue, must call Vue.use(Vuex) before creating a store instance.
)
assert(typeof Promise !== 'undefined', vuex requires a Promise polyfill in this browser.
)
assert(this instanceof Store, store must be called with the new operator.
)
}
const {
plugins = [],
strict = false
} = options
// store internal state
//下方是在Vuex的this上挂载一些对象,这里暂且不要知道他们要来干什么
// 因为是源码分析,不要所有的代码都清除,第一次源码分析,你就只当他们是挂载对象,往下看
this._committing = false
this._actions = Object.create(null)
this._actionSubscribers = []
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
// ModuleCollection代表模块收集,形成模块树
this._modules = new ModuleCollection(options) //碰到第一个不是定义空对象,debugger进去,分析在下面
this._modulesNamespaceMap = Object.create(null)
this._subscribers = []
this._watcherVM = new Vue()
this._makeLocalGettersCache = Object.create(null)
// bind commit and dispatch to self
const store = this
const { dispatch, commit } = this
this.dispatch = function boundDispatch (type, payload) { // 绑定dispatch的指针为store
return dispatch.call(store, type, payload)
}
this.commit = function boundCommit (type, payload, options) { // 绑定commit的指针为store
return commit.call(store, type, payload, options)
}
// strict mode
this.strict = strict
const state = this._modules.root.state // 获取到用户定义的state
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
// 初始化root模块 注册getters,actions,mutations 子模块
installModule(this, state, [], this._modules.root)
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
// 初始化vm 用来监听state getter
resetStoreVM(this, state)
// apply plugins
// 插件的注册
plugins.forEach(plugin => plugin(this))
// 下方的是开发工具方面的 暂不提
const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools
if (useDevtools) {
devtoolPlugin(this)
}
}
}
2.1 new ModuleCollection
ModuleCollection函数在src目录下的module目录下的module-collection.js文件下
export default class ModuleCollection {
constructor (rawRootModule) { // rawRootModule === options
// register root module (Vuex.Store options)
this.register([], rawRootModule, false)
}
}
2.1.1 register()
register (path, rawModule, runtime = true) {
// register([],options,false)
if (process.env.NODE_ENV !== 'production') {
assertRawModule(path, rawModule) // 开发环境断言,暂忽略
}
const newModule = new Module(rawModule, runtime)
if (path.length === 0) { // path长度为0,为根节点,将newModule 赋值为root
this.root = newModule
} else {
const parent = this.get(path.slice(0, -1))
parent.addChild(path[path.length - 1], newModule)
}
// register nested modules
if (rawModule.modules) { // 如果存在子模块,递归调用register,形成一棵树
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(path.concat(key), rawChildModule, runtime)
})
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2.1.2 new Module()
Module函数在同目录下的modele.js文件下
export default class Module {
// new Module(options,false)
constructor (rawModule, runtime) {
this.runtime = runtime
// Store some children item
this._children = Object.create(null)
// Store the origin module object which passed by programmer
this._rawModule = rawModule // 将options放到Module上 用_raModele上
const rawState = rawModule.state // 将你定义的state取出
// Store the origin module's state
// 如果你定义的state为函数,调用函数,为对象,则取对象 赋值为module上的state上
this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
}
}
所以Module的this内容为如下:
2.1.3 installModule
function installModule (store, rootState, path, module, hot) {
// installModule(Vuex,state,[],module) module是前面 new ModuleCollection产生的对象
const isRoot = !path.length
const namespace = store._modules.getNamespace(path)
// register in namespace map
if (module.namespaced) {
if (store._modulesNamespaceMap[namespace] && process.env.NODE_ENV !== 'production') {
console.error([vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')}
)
}
store._modulesNamespaceMap[namespace] = module
}
// set state
if (!isRoot && !hot) {
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() => {
if (process.env.NODE_ENV !== 'production') {
if (moduleName in parentState) {
console.warn(
[vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"
)
}
}
Vue.set(parentState, moduleName, module.state)
})
}
// 设置当前上下文
const local = module.context = makeLocalContext(store, namespace, path)
// 注册mutation
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
// 注册action
module.forEachAction((action, key) => {
const type = action.root ? key : namespace + key
const handler = action.handler || action
registerAction(store, type, handler, local)
})
// 注册getter
module.forEachGetter((getter, key) => {
const namespacedType = namespace + key
registerGetter(store, namespacedType, getter, local)
})
// 逐一注册子module
module.forEachChild((child, key) => {
installModule(store, rootState, path.concat(key), child, hot)
})
}
2.1.4 makeLocalContext
// 设置module的上下文,绑定对应的dispatch、commit、getters、state
function makeLocalContext (store, namespace, path) {
const noNamespace = namespace === ''
const local = {
//noNamespace 为true 使用原先的 至于后面的 不知道干啥用的 后面继续研究
dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {
const args = unifyObjectStyle(_type, _payload, _options)
const { payload, options } = args
let { type } = args
if (!options || !options.root) {
type = namespace + type
if (process.env.NODE_ENV !== 'production' && !store._actions[type]) {
console.error([vuex] unknown local action type: ${args.type}, global type: ${type}
)
return
}
}
return store.dispatch(type, payload)
},
commit: noNamespace ? store.commit : (_type, _payload, _options) => {
//noNamespace 为true 使用原先的
const args = unifyObjectStyle(_type, _payload, _options)
const { payload, options } = args
let { type } = args
if (!options || !options.root) {
type = namespace + type
if (process.env.NODE_ENV !== 'production' && !store._mutations[type]) {
console.error([vuex] unknown local mutation type: ${args.type}, global type: ${type}
)
return
}
}
store.commit(type, payload, options)
}
}
// getters and state object must be gotten lazily
// because they will be changed by vm update
Object.defineProperties(local, {
getters: {
get: noNamespace
? () => store.getters
: () => makeLocalGetters(store, namespace)
},
state: {
get: () => getNestedState(store.state, path)
}
})
return local
}
function getNestedState (state, path) {
return path.reduce((state, key) => state[key], state)
}
2.1.5 registerMutation
mutation的注册,会调用下方方法,将wrappedMutationHandler函数放入到entry中
function registerMutation(store, type, handler, local) {
// entry和store._mutations[type] 指向同一个地址
var entry = store._mutations[type] || (store._mutations[type] = []);
entry.push(function wrappedMutationHandler(payload) {
handler.call(store, local.state, payload);
});
}
2.1.6 forEachAction
action的注册,会调用下方方法,将wrappedActionHandler函数放入到entry中
function registerAction(store, type, handler, local) {
var entry = store._actions[type] || (store._actions[type] = []);
// entry和store._actions[type]指向同一个地址
entry.push(function wrappedActionHandler(payload) {
var res = handler.call(store, {
dispatch: local.dispatch,
commit: local.commit,
getters: local.getters,
state: local.state,
rootGetters: store.getters,
rootState: store.state
}, payload);
if (!(0, _util.isPromise)(res)) {
res = Promise.resolve(res);
}
if (store._devtoolHook) {
return res.catch(function (err) {
store._devtoolHook.emit('vuex:error', err);
throw err;
});
} else {
return res;
}
});
}
因为entry和上面的_action[type],_mutations[type] 指向同一个地址,所以:
2.1.7 forEachGetter
getter的注册,会调用下方方法
function registerGetter(store, type, rawGetter, local) {
if (store._wrappedGetters[type]) {
if (true) {
console.error('[vuex] duplicate getter key: ' + type);
}
return;
}
store._wrappedGetters[type] = function wrappedGetter(store) {
return rawGetter(local.state, // local state
local.getters, // local getters
store.state, // root state
store.getters // root getters
);
};
}
2.1.8 resetStoreVM
function resetStoreVM (store, state, hot) {
const oldVm = store._vm //将_vm用变量保存
// bind store public getters
store.getters = {}
// reset local getters cache
store._makeLocalGettersCache = Object.create(null)
const wrappedGetters = store._wrappedGetters // 获取installModule方法完成的_wrappedGetters内容
const computed = {}
forEachValue(wrappedGetters, (fn, key) => {
// use computed to leverage its lazy-caching mechanism
// direct inline function use will lead to closure preserving oldVm.
// using partial to return function with only arguments preserved in closure environment.
computed[key] = partial(fn, store)
Object.defineProperty(store.getters, key, {
// 拦截get返回store._vm[key]中的值,即可以通过 this.$store.getters.xxx访问值
get: () => store._vm[key],
enumerable: true // for local getters
})
})
// use a Vue instance to store the state tree
// suppress warnings just in case the user has added
// some funky global mixins
const silent = Vue.config.silent
// silent设置为true,取消所有日志警告等
Vue.config.silent = true
store._vm = new Vue({ // 将state,getter的值进行监听,从这里就可以看出getter其实就是采用的vue的computed
data: {
$$state: state
},
computed
})
// 恢复原先配置
Vue.config.silent = silent
// enable strict mode for new vm
if (store.strict) {
enableStrictMode(store)
}
// 若存在旧的实例,解除对state的引用,等dom更新后把旧的vue实例销毁
if (oldVm) {
if (hot) {
// dispatch changes in all subscribed watchers
// to force getter re-evaluation for hot reloading.
store._withCommit(() => {
oldVm._data.$$state = null
})
}
Vue.nextTick(() => oldVm.$destroy())
}
}
看到这里,你应该对vuex有初步的了解
// 这也是我们为什么能用访问到state,getter的原因
//this.store.dispatch('xxx') ,this.$store.dispatch('xxx')
1
2
相信你也有点乱,其实上面很多我没讲到的不是我不想讲,是具体我也不知道干啥的,看源码学习呢,看主要就行,后面理解多了,其他的就慢慢都会,你不可能刚开始看,就每一行,他写的每一句的用途都知道是干什么的,只能先看主要方法,在慢慢琢磨,一步步来吧,别急,现在我来画一个流程图,更好的去理解吧。
2.1.9 流程图
3.连贯
import Vue from 'vue'
import Counter from './Counter.vue'
import store from './store'
new Vue({
el: '#app',
store,
render: h => h(Counter)
})
当运行new Vue的时候,传入了store,前面minix beforecreate,执行到beforecreate钩子时,会调用vueInit函数,就可以在this.$store取到store对象了,因为options.store有值了 ,不为空,这样就连贯到了,所以这就是为什么可以用this.$store取值。
HTML常用meta大全
Meta标签是HTML语言head区的一个辅助性标签,它位于HTML文档头部的head标记和title标记之间,它提供用户不可见的信息。
Meta : 即 元数据(Metadata)是数据的数据信息。
元数据可以被使用浏览器(如何显示内容或重新加载页面),搜索引擎(关键词),或其他 Web 服务调用。
用我们的大白话来说,它本身是一个没什么用的标签,但是一旦在它内部通过其他属性设置了某些效果,它就起作用了,所以我们称之为“ 元数据 ”。
Code
<!-- 定义文档的字符编码 -->
<meta charset="utf-8" />
<!-- 强制Chromium内核,作用于360浏览器、QQ浏览器等国产双核浏览器 -->
<meta name="renderer" content="webkit"/>
<!-- 强制Chromium内核,作用于其他双核浏览器 -->
<meta name="force-rendering" content="webkit"/>
<!-- 如果有安装 Google Chrome Frame 插件则强制为Chromium内核,否则强制本机支持的最高版本IE内核,作用于IE浏览器 -->
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"/>
<!--
设置视窗大小
width 设置layout viewport 的宽度,为一个正整数,或字符串"width-device"
initial-scale 设置页面的初始缩放值,为一个数字,可以带小数
minimum-scale 允许用户的最小缩放值,为一个数字,可以带小数
maximum-scale 允许用户的最大缩放值,为一个数字,可以带小数
shrink-to-fit=no IOS9中要想前面的属性起作用需要加上这个
height 设置layout viewport 的高度,这个属性对我们并不重要,很少使用
user-scalable 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许
-->
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<!-- 页面描述 -->
<meta name="description" content="不超过150个字符"/>
<!-- 页面关键词 -->
<meta name="keywords" content=""/>
<!-- 网页作者 -->
<meta name="author" content="name, email@gmail.com"/>
<!--
搜索引擎抓取
all:文件将被检索,且页面上的链接可以被查询;
none:文件将不被检索,且页面上的链接不可以被查询;
index:文件将被检索;
follow:页面上的链接可以被查询;
noindex:文件将不被检索;
nofollow:页面上的链接不可以被查询。
-->
<meta name="robots" content="index,follow"/>
<!-- 忽略页面中的数字识别为电话,忽略email识别-->
<meta name="format-detection" content="telphone=no, email=no"/>
<!-- IOS begin -->
<!-- 添加到主屏后的标题(iOS 6 新增) -->
<meta name="apple-mobile-web-app-title" content="标题">
<!-- 当网站添加到主屏幕快速启动方式,可隐藏地址栏,仅针对ios的safari (ios7.0版本以后,safari上已看不到效果) -->
<meta name="apple-mobile-web-app-capable" content="yes"/>
<!-- 是否启用 WebApp 全屏模式,删除苹果默认的工具栏和菜单栏 -->
<meta name="apple-touch-fullscreen" content="yes"/>
<!-- 添加智能 App 广告条 Smart App Banner(iOS 6+ Safari) -->
<meta name="apple-itunes-app" content="app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL">
<!-- 设置苹果工具栏颜色:默认值为 default(白色),可以定为 black(黑色)和 black-translucent(灰色半透明) -->
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
<!-- 不让百度转码 -->
<meta http-equiv="Cache-Control" content="no-siteapp" />
<!-- 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓 -->
<meta name="HandheldFriendly" content="true">
<!-- 微软的老式浏览器 -->
<meta name="MobileOptimized" content="320">
<!-- uc强制竖屏 -->
<meta name="screen-orientation" content="portrait">
<!-- QQ强制竖屏 -->
<meta name="x5-orientation" content="portrait">
<!-- UC强制全屏 -->
<meta name="full-screen" content="yes">
<!-- QQ强制全屏 -->
<meta name="x5-fullscreen" content="true">
<!-- UC应用模式 -->
<meta name="browsermode" content="application">
<!-- QQ应用模式 -->
<meta name="x5-page-mode" content="app">
<!-- windows phone 点击无高光 -->
<meta name="msapplication-tap-highlight" content="no">
<!--
iOS 图标 begin
网站添加至ios桌面时的图标
-->
<!-- iPhone 和 iTouch,默认 57x57 像素,必须有 -->
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="table_ico57.png">
<!-- Retina iPhone 和 Retina iTouch,114x114 像素,可以没有,但推荐有 -->
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="table_ico72.png">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="table_ico114.png">
<!-- Retina iPad,144x144 像素,可以没有,但推荐有 -->
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="table_ico144.png">
<!-- iOS 启动画面 begin -->
<!-- iPad 竖屏 768 x 1004(标准分辨率) -->
<link rel="apple-touch-startup-image" sizes="768x1004" href="/splash-screen-768x1004.png"/>
<!-- iPad 横屏 1024x748(标准分辨率) -->
<link rel="apple-touch-startup-image" sizes="1024x748" href="/Default-Portrait-1024x748.png"/>
<!-- iPad 竖屏 1536x2008(Retina) -->
<link rel="apple-touch-startup-image" sizes="1536x2008" href="/splash-screen-1536x2008.png"/>
<!-- iPad 横屏 2048x1496(Retina) -->
<link rel="apple-touch-startup-image" sizes="2048x1496" href="/splash-screen-2048x1496.png"/>
<!-- iPhone/iPod Touch 竖屏 320x480 (标准分辨率) -->
<link rel="apple-touch-startup-image" href="/splash-screen-320x480.png"/>
<!-- iPhone/iPod Touch 竖屏 640x960 (Retina) -->
<link rel="apple-touch-startup-image" sizes="640x960" href="/splash-screen-640x960.png"/>
<!-- iPhone 5/iPod Touch 5 竖屏 640x1136 (Retina) -->
<link rel="apple-touch-startup-image" sizes="640x1136" href="/splash-screen-640x1136.png"/>
<!-- IOS end -->
<!-- Windows 8 磁贴颜色 -->
<meta name="msapplication-TileColor" content="#000"/>
<!-- Windows 8 磁贴图标 -->
<meta name="msapplication-TileImage" content="icon.png"/>
<!-- 添加 RSS 订阅 -->
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml"/>
<!-- sns 社交标签 begin -->
<!-- 参考微博API -->
<meta property="og:type" content="类型" />
<meta property="og:url" content="URL地址" />
<meta property="og:title" content="标题" />
<meta property="og:image" content="图片" />
<meta property="og:description" content="描述" />
<!-- sns 社交标签 end -->
低版本IE浏览器访问问题
添加好强制Webkit内核的代码,使用国产浏览器访问网站已经不存在IE兼容问题了,IE访客量将大大减少。但仍然不可避免会有一些老旧电脑通过低版本IE浏览器访问,如果我们专门为了这极小部分用户进行 CSS HACK ,将严重加重我们的工作量。
更何况自2016年1月起微软已经停止为IE11以下版本提供支持和更新,已经这么久没有更新,低版本IE不仅对CSS3和HTML5支持有问题,更有安全问题。
那么,我们不去支持低版本IE,这小部分用户怎么办呢?
我们可以使用 if IE 语句给网站添加IE升级提示,提示用户进行浏览器升级,或者切换更先进的浏览器再访问。
我们可以在刚刚的meta标签下添加一段跳转到IE升级提示页的代码(当IE版本低于IE11时跳转),实现低版本IE用户访问时提示他们进行升级或者更换浏览器。
强制Webkit内核和提示低版本IE访问用户升级完整代码如下所示,把这段代码添加到头部模板文件标签下即可:
<meta name="renderer" content="webkit"/>
<meta name="force-rendering" content="webkit"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<script>/@cc_on window.location.href="http://support.dmeng.net/upgrade-your-browser.html?referrer="+encodeURIComponent(window.location.href); @/</script>
1
2
3
4
(@cc_on 是 IE10 及更旧版IE特有的条件编译语句,因此可以用来判断是否除 IE11 以外的其他IE版本。)
因为低版本IE访问时因为不兼容CSS3和HTML5网站往往是错版的,添加了上面这段代码,当低版本IE用户访问时就会跳转到升级提示页,避免不必要的资源加载,降低网站服务器开销。
测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<meta name="renderer" content="webkit"/>
<meta name="force-rendering" content="webkit"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<script>/@cc_on window.location.href="http://support.dmeng.net/upgrade-your-browser.html?referrer="+encodeURIComponent(window.location.href); @/</script>
</head>
<body>
<h1>Hello world</h1>
</body>
</html>
IE 11 会正常输出
IE 10 将会看到以下页面
参考
前端 Meta 用法大汇总 - MR_LIXP
通过meta代码强制浏览器使用WebKit内核极速模式(解决 meta name=“renderer” content=“webkit” 不起作用)- 艾欢欢
概述
replace的参数是char和CharSequence,即可以支持字符的替换,也支持字符串的替换(CharSequence即字符串序列的意思,说白了也就是字符串)
replaceAll的参数是regex,即基于规则表达式的替换,比如:可以通过replaceAll("\d", “*”)把一个字符串所有的数字字符都换成星号
相同点
相同点:都是全部替换,即把源字符串中的某一字符或字符串全部换成指定的字符或字符串
不同点
不同点:replaceAll支持正则表达式,因此会对参数进行解析(两个参数均是),如replaceAll("\d", “"),而replace则不会,replace("\d","”)就是替换"\d"的字符串,而不会解析为正则
另外还有一个不同点:“\”在java中是一个转义字符,所以需要用两个代表一个。例如System.out.println( “\” ) ;只打印出一个""。但是“\”也是正则表达式中的转义字符,需要用两个代表一个。所以:\被java转换成\,\又被正则表达式转换成\,因此用replaceAll替换“\”为"\",就要用replaceAll("\","\\"),而replace则replace("\","\")
如果只想替换第一次出现的,可以使用replaceFirst(),这个方法也是基于规则表达式的替换,但与replaceAll()不同的是,只替换第一次出现的字符串
HTTP响应状态码
200 OK
201 Created
202 Accepted
203 Non-Authoritative Information
204 No Content
205 Reset Content
206 Partial Content
207 Multi-Status (WebDAV)
226 IM Used (HTTP Delta encoding)
200 OK
请求成功。成功的含义取决于HTTP方法:
GET:资源已被提取并在消息正文中传输。
HEAD:实体标头位于消息正文中。
POST:描述动作结果的资源在消息体中传输。
TRACE:消息正文包含服务器收到的请求消息
PUT 和 DELETE 的请求成功通常并不是响应200 OK的状态码而是 204 No Content 表示无内容(或者 201 Created表示一个资源首次被创建成功)。
201 Created
表示请求已经被成功处理,并且创建了新的资源。
这通常是在POST请求,或是某些PUT请求之后返回的响应。
新的资源在应答返回之前已经被创建。同时新增的资源会在应答消息体中返回,其地址是原始请求的路径或者是 Location 首部的值。
202 Accepted
表示服务器端已经收到请求消息,但是尚未进行处理。
但是稍后无法通过 HTTP 协议给客户端发送一个异步请求来告知其请求的处理结果。该状态码适用于将请求交由另外一个进程或者服务器来进行处理,或者是对请求进行批处理的情形。
203 Non-Authoritative Information
表示服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝。
当前的信息可能是原始版本的子集或者超集。例如,包含资源的元数据可能导致原始服务器知道元信息的超集。
使用此状态码不是必须的,而且只有在响应不使用此状态码便会返回200 OK的情况下才是合适的。
204 No Content
表示该请求已经成功了,但是客户端客户不需要离开当前页面。
默认情况下 204 响应是可缓存的。一个 ETag 标头包含在此类响应中。
使用惯例是:
在 PUT 请求中进行资源更新,但是不需要改变当前展示给用户的页面,那么返回 204 No Content。
如果创建了资源,则返回 201 Created 。
如果应将页面更改为新更新的页面,则应改用 200 。
205 Reset Content
用来通知客户端重置文档视图,比如清空表单内容、重置 canvas 状态或者刷新用户界面。
与204响应一样,该响应也被禁止包含任何消息体,且以消息头后的第一个空行结束。
206 Partial Content
服务器已经成功处理了部分 GET 请求。
类似于 FlashGet 或者迅雷这类的 HTTP 下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。
该请求必须包含 Range 头信息来指示客户端希望得到的内容范围,并且可能包含 If-Range 来作为请求条件。
如果只包含一个数据区间,那么整个响应的 Content-Type 首部的值为所请求的文件的类型,同时包含 Content-Range 首部。
示例:
HTTP/1.1 206 Partial Content
Date: Wed, 15 Nov 2015 06:25:24 GMT
Last-Modified: Wed, 15 Nov 2015 04:58:08 GMT
Content-Range: bytes 21010-47021/47022
Content-Length: 26012
Content-Type: image/gif
... 26012 bytes of partial image data ...
包含多个数据区间,那么整个响应的Content-Type首部的值为multipart/byteranges,其中一个片段对应一个数据区间,并提供 Content-Range 和 Content-Type 描述信息。
示例:
HTTP/1.1 206 Partial Content
Date: Wed, 15 Nov 2015 06:25:24 GMT
Last-Modified: Wed, 15 Nov 2015 04:58:08 GMT
Content-Length: 1741
Content-Type: multipart/byteranges; boundary=String_separator
--String_separator
Content-Type: application/pdf
Content-Range: bytes 234-639/8000
...the first range...
--String_separator
Content-Type: application/pdf
Content-Range: bytes 4590-7999/8000
...the second range
--String_separator--
207 Multi-Status (WebDAV)
代表之后的消息体将是一个XML消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码。由WebDAV(RFC 2518)扩展的状态码.
226 IM Used (HTTP Delta encoding)
服务器已经完成了对资源的 GET 请求,并且响应是对当前实例应用的一个或多个实例操作结果的表示。
在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中。他有很多优秀的特性,例如拦截请求和响应、取消请求、转换json、客户端防御cSRF等。
一 . 安装
npm install axios;
1
二 . 引入
在项目的src目录中,新建一个request文件夹,然后在里面新建一个http.js和一个api.js文件。http.js文件用来封装我们的axios,api.js用来统一管理我们的接口。
三 . 开始封装
在http.js中引入axios . vue及其他
import Axios from 'axios'; // 引入axios
import { Message, Loading, Notification } from 'element-ui'
import vue from 'vue';
1
2
3
http.js文件全部代码如下:
import Axios from 'axios';
import store from '../store';
import { Message, Loading, Notification } from 'element-ui'
import vue from 'vue';
// 环境的切换
if (process.env.NODE_ENV == 'development') {
Axios.defaults.baseURL = "http://10.230.39.58:33390/devops";
}
else if (process.env.NODE_ENV == 'production') {
Axios.defaults.baseURL = "http://10.230.39.58:33390/devops";
}
// request请求拦截器
Axios.defaults.withCredentials = true
vue.prototype.$axios = Axios
//请求超时时间
// Axios.defaults.timeout = 100000;
Axios.defaults.headers.get['Content-Type'] = "application/json"
Axios.interceptors.request.use(config => {
const Basic = sessionStorage.getItem("basicParam")
if (Basic) {
config.headers.Authorization = Basic ${Basic}
;
} else {
console.log("无校验值");
}
return config;
}, error => {
Promise.reject(error);
})
// respone返回拦截器
Axios.interceptors.response.use(
response => {
if (response.data.code !== 200) {
Notification.error({
title: '错误',
message: response.data.message
});
}
return response.data;
}, error => {
// Notification.error({
// title: '错误',
// message: '系统异常'
// });
console.log('err' + error);// for debug
return Promise.reject(error);
}
)
export default Axios;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
api.js文件全部代码如下:
import axios from 'axios';
/
封装get方法
@param url
@param data
@returns {Promise}
*/
export function fetch(url, params = {}) {
return new Promise((resolve, reject) => {
axios.get(url, {
params: params
})
.then(response => {
resolve(response.data);
})
.catch(err => {
reject(err)
})
})
}
/*
封装post请求
@param url
@param data
@returns {Promise}
/
export function _post(url, data = {}) {
return new Promise((resolve, reject) => {
axios.post(url, data)
.then(response => {
console.log(response,
"response");
resolve(response);
}, err => {
reject(err)
})
})
}
/
实际业务比较复杂不便展示,写一个简单的demo记录此功能
遍历此div:
<div v-for="item in demoArray">
<input type="text" v-model="item.name">
<el-switch class="exio-switch" v-model="item.status" active-text="开" inactive- text="关" active-color="#13ce66"></el-switch>
<button @click="showInfo(item)">查看</button>
</div>
js代码:
new Vue({
el: '#app',
data() {
return {
demoArray: [],
};
},
created() {
// 生成模拟数据
for (let i = 0; i < 5; i++) {
let e = {};
e.name = "div"+i;
e.status = true;
this.demoArray.push(e);
}
},
methods: {
showInfo(item) {
console.log(item.name);
console.log(item.status);
}
}
})
页面展示:
修改一条数据:
验证双向绑定结果:
为了解决不确定数量的数据(数据来源可能是接口等)的展示和操作,将每条数据作为元素放在数组中,通过数组中元素的属性来进行双向绑定。
整理的仓促,emmm,收工
项目介绍
本项目总体分为
平台搭建
模拟数据源生成
实时流数据处理
实时数据大屏
这几个部分,我将分成几个博客分别介绍这些部分的工作,本文主要介绍最后一个部分,实时数据大屏。
前面的几篇文章已经将平台的搭建,数据模拟生成,流数据处理部分做了详细的介绍,这篇文章主要是对前面所做的工作进行一个升华,关分析出数据不够直观,而能将所做的东西更加直观的表达出来就需要进行可视化了,下面我将为大家介绍可视化部分的工作
平台搭建,具体可以看平台搭建
模拟数据源生成,具体可以看模拟数据源生成
实时流数据处理,具体可以看实时流数据处理
项目下载地址下载
环境介绍
首先还是对环境介绍一下,这部分主要使用的将是html,php,js,css等做网站所需要的一些语言及工具,由于需要进行异步数据加载,所以还需要一个本地的服务器,本文使用的是phpstudy,主要是这个工具还集成了mysql,能简化不少我们的工作,当然如果自己拥有服务器,那完全是可以将这个部署在服务器上面的
首先我们先要安装phpstudy,这里不对具体的安装过程进行介绍,安装完成后我们可以进入网站的根目录
在这个目录下新建一个目录即可作为我们的网站目录了
然后我们可以使用phpstudy带的站点域名管理为我们的网站设置一个域名,其中的网站目就是我们刚刚创建的网站目录
在hosts里面是需要加入IP和域名的映射的,如:
127.0.0.1 www.sshstudy3.com
1
这样就可以在浏览器里面通过访问域名来访问我们要做的网站了
接下来我们需要去创建数据库,打开phpMyAdmin,我们可以进入数据库管理界面
这里的账号密码默认都是root
进入后我们可以看到如下界面
在这里可以创建数据库,或者进行数据库的访问等,不再赘述
到这里基本需要使用到的环境就基本完成了,接下来就是代码的部分
首先了解一下递归的定义:
递归:递归函数自己调用自己,且函数内部必须有结束条件、否则就是一个死循环;
递归案例:求 n 的阶乘 (循环 || 递归)
阶乘公式先了解一下:
即n的阶乘 = n(n-1)的阶乘,如归使用for循环来做这件事件就很简单:
//for循环
function fact(n) {
let end = 1;
for (var i = 1; i <= n; i++) {
end = i
}
return end
}
console.log(fact(5)) //5的阶乘 120
再看看递归的做法:
//递归
function fact(n) {
if (n === 1) {
return 1 //结束条件
}
return n fact(n - 1) //此处的fact函数相当于当前队列的阶乘
}
console.log(fact(5)) //5的阶乘
解析: 公式 n(n-1)! 则函数内部只需要返回 n该函数 n-1,
即 n(n-1)! == nfact(n-1)
看一下内部队列顺序,当形参为5时 阶乘为 5 fact(n-1),直至形参n = 1时,fact函数有了返回值 1,有了结束条件后整个函数结束自掉,返回阶乘结果。
递归的优点:递归的实现明显要比循环简单得多。
递归的缺点:
1、效率低:递归由于是函数自己掉自己,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间。
2、性能差:调用栈可能会溢出,每次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。
总结:对于JavaScript而言,能用循环解决的事情、尽量不要考虑递归、 慎用!
如果说 npm 的大新闻,莫过于之前的 left-pad 撤包事件,event-stream 投毒事件,Ant Design 彩蛋事件。使得很多前端开发者又开始重新思考 npm 生态时候真的存在问题?
今天我们文章的主角是 memwatch,一个用来帮助我们检查 Node.js 是否存在内存泄漏的库,和这个库传奇的一生。
2012 年 02 月 06 日,一位 Mozilla 的工程师 lloyd 创建了这个库,并写了一篇博文“Is My NodeJS Program Leaking?”(我的 Node.js 程序是否存在内存泄漏?)。这个包最初被命名为 gcstats,代码上传到了 github。
6 月 27 日,npm 包改名为 memwatch,发布 0.1.1 版。
7 月 4 日,lloyd 为这个库添加了开源许可协议:WTFPL,发布 0.1.2 版。很多人对这个开源许可协议可能比较陌生,WTFPL 的全称是 Do What The Fuck You Want To Public License,中文译名:你他妈的想干嘛就干嘛公共许可证。也许就是这份协议开启了 memwatch 库不寻常的一生。
2013 年 3 月 14 日,作者 lloyd 提交了最后一次代码,发布了 0.2.2 版本。支持的 Node.js 版本为 0.6.0。随后这个库再也没有更新过。
从作者的博文和推文可以看到,作者在 2014 年离开了 Mozilla。而从作者的 github 动态更可以看出,作者应该是转入了 golang 阵营。
2014 年 6 月 28 日,作者的一位前同事 deepak1556 fork 了这个库,增加了对 Node.js 0.11 的支持,并发起了合并请求。但是作者并没有回复,也没有合并此次请求。此时距离原作者放弃这个库也已经过去一年多了。
2015 年 2 月 7 日,marcominetti 又 fork 了 deepak1556 的库,增加了对 Node.js 0.12 的支持,并向原库发起了合并请求,同样没有得到作者的任何回复。于是 marcominetti 决定自立门户,于是将 memwatch 改名为 memwatch-next 发布到了 npm。
2017 年 1 月 27 日,如同前两位维护者一样,marcominetti 也最终放弃了继续更新这个库。到此时,此库支持的 Node.js 版本为 4、5、6。
2018 年 5 月 6 日,eduardbcom 又 fork 了 marcominetti 的库,增加了 Node.js 9 的支持,并且放弃了对 Node.js 9 以下所有版本的支持。改名为 node-memwatch 并发布到了 npm。随后再也没有更新过代码。
2018 年 7 月 17 日,一位开发者 dyatko 又 fork 了 eduardbcom 的库,增加了对 Node.js 8 的支持,并向原库发起了合并请求,同样没有得到作者的任何回复。
但在此次 pr 的评论中,另一位开发者说,airbnb 也 fork 了 marcominetti 的库,并改名为 @airbnb/node-memwatch 发布到了 npm。
有了大厂接手,也算是这个库最终的归宿吧。
相关阅读
开发者对 npm 公司不满,unpublish 了自己的所有模块
月下载量千万的 npm 包被黑客篡改,Vue 开发者可能正在遭受攻击
驳《我不是很懂 Node.js 社区的 DRY 文化》
机器人伪装成人类在 GitHub 上为开源项目修复 bug
蓝蓝设计的小编 http://www.lanlanwork.com