thymeleaf与vue结合使用时,vue如何取模板里的值

<li th:each="grade : ${grades}" th:v-bind:class="|{current: gradeId==${grade.id}}|">

<a th:title="${grade.name}" href="javascript:void(0)"  th:id="${grade.id}"
  th:text="${grade.name}"  th:@click="|getCourses(${grade.id},subjectId,1)|"
  >二年级</a></li>
th:@click="|getCourses(${grade.id},subjectId,1)|"

@clickVUE里绑定的点击事件,此时事件存在于thymeleaf的循环th:each下的元素,getCourses()vue里的方法属于js,但是需要取到模板里产生的值<年级id>

此时可以用th:v-on:"| |" 或者th:@click="| |" 简单来说就是将前端的方法当作字符串拼接起来,前面加th:就能解析${grade.id} 的值

th:v-bind:class="|{current: gradeId==${grade.id}}|"

同理,绑定class用于样式也能如此

参考链接


thymeleaf 与 vue 结合使用时,vue如何取模板里的值

Vue axios 发送 FormData 请求

一、简介
axios 默认是 Payload 格式数据请求,但有时候后端接收参数要求必须是 Form Data 格式的,所以我们就得进行转换。

Payload 和 Form Data 的主要设置是根据请求头的 Content-Type 的值来的:

Payload:

Content-Type: 'application/json; charset=utf-8'

Form Data:

Content-Type: 'application/x-www-form-urlencoded'

Content-Type: 'multipart/form-data'

上面三种 Content-Type 值介绍

application/json 和 application/x-www-form-urlencoded 都是表单数据发送时的编码类型。

form 的 enctype 属性为编码方式,常用有两种:application/x-www-form-urlencoded 和multipart/form-data,默认为 application/x-www-form-urlencoded。

当 action 为 get 时候,浏览器用 x-www-form-urlencoded 的编码方式把 form 数据转换成一个字串(name1=value1&name2=value2...),然后把这个字串 append 到 url 后面,用 ?分割,加载这个新的 url。

当 action 为 post 时候,浏览器把 form 数据封装到 http body 中,然后发送到 server。

如果没有 type=file 的控件,用默认的 application/x-www-form-urlencoded 就可以了。

但是如果有 type=file 的话,就要用到 multipart/form-data 了。浏览器会把整个表单以控件为单位分割,并为每个部分加上 Content-Disposition(form-data或者file)、Content-Type(默认为text/plain)、name(控件name) 等信息,并加上分割符 (boundary)。

二、发送 formdata 请求(下面有这几种方式格式化参的数据样本,用于参考比较,看需求选择方式)
方式一,自己封装一个格式化函数:

import axios from 'axios'

################################### 请求方式一,全局使用
// 创建 axios 实例
const service = axios.create({
  baseURL: '',
  timeout: 20000,
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
})

// 将请求数据转换成功 formdata 接收格式
service.defaults.transformRequest = (data) => {
  return stringify(data)
}

################################### 请求方式二,局部使用
axios({
  method: 'post',
  url: 'http://localhost:8080/dzm',
  data: {
    username: 'dzm',
    password: 'dzm123456'
  },
  transformRequest: [
    function (data) {
      // 将请求数据转换成功 formdata 接收格式
      return stringify(data)
    }
  ],
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
})

################################### 转换方法封装

// 将参数转换成功 formdata 接收格式
function stringify (data) {
  const formData = new FormData()
  for (const key in data) {
    // eslint-disable-next-line no-prototype-builtins
    if (data.hasOwnProperty(key)) {
      if (data[key]) {
        if (data[key].constructor === Array) {
          if (data[key][0]) {
            if (data[key][0].constructor === Object) {
              formData.append(key, JSON.stringify(data[key]))
            } else {
              data[key].forEach((item, index) => {
                formData.append(key + `[${index}]`, item)
              })
            }
          } else {
            formData.append(key + '[]', '')
          }
        } else if (data[key].constructor === Object) {
          formData.append(key, JSON.stringify(data[key]))
        } else {
          formData.append(key, data[key])
        }
      } else {
        if (data[key] === 0) {
          formData.append(key, 0)
        } else {
          formData.append(key, '')
        }
      }
    }
  }
  return formData
}

方式二,使用 qs 组件,但是 qs 格式化会过滤空数组数据:

import axios from 'axios'
// qs 模块是安装 axios 模块的时候就有的,不用另行安装,通过 import 引入即可使用
import qs from 'qs'

################################### 请求方式一,全局使用

// 创建 axios 实例
const service = axios.create({
  baseURL: '',
  timeout: 20000,
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
})

// 将请求数据转换成功 formdata 接收格式,这一段选方式一那种拦截转换也可以。
service.interceptors.request.use(config => {
  const token = Vue.ls.get(ACCESS_TOKEN)
  if (token) {
    // 让每个请求携带自定义 token 请根据实际情况自行修改
    config.headers['X-Token'] = token
  }
  // 将请求数据转换成功 formdata 接收格式
  config.data = qs.stringify(config.data)
  return config
}, err)

################################### 请求方式二,局部使用
axios({
  method: 'post',
  url: 'http://localhost:8080/dzm',
  data: {
    username: 'dzm',
    password: 'dzm123456'
  },
  transformRequest: [
    function (data) {
      // 将请求数据转换成功 formdata 接收格式
      return qs.stringify(data)
    }
  ],
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
})

方式三,数组会被转换成字符串(这种不是特殊情况一般不会使用上)

import axios from 'axios'

################################### 请求方式跟上面一样
axios({
  method: 'post',
  url: 'http://localhost:8080/dzm',
  data: {
    username: 'dzm',
    password: 'dzm123456'
  },
  transformRequest: [
    function (data) {
      // 将请求数据转换成功 formdata 接收格式
      return stringify(data)
    }
  ],
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
})

################################### 转换方法封装

// 将参数转换成功 formdata 接收格式
function stringify (data) {
  let ret = ''
  for (const it in data) {
    ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
  }
  ret = ret.substring(0, ret.lastIndexOf('&'))
  return ret
}

三、上面方式,参数格式化之后:
方式一 格式化出来的数据:

// 数组无值
id: 2086
intention: 
follower_id[]: 
concat_material[]:

// 数组有值
id: 2086
intention: 
follower_id[0]: 351
follower_id[1]: 66
// 数组 json 为空会被转成正常的数组,有值会被转成字符串,所以服务器需要注意处理
concat_material: [{"fname":"视频订单.xls","key":"local/other/099f4be38fb8e69bb031cbc36ed283a6.xls"}]

方式二 格式化出来的数据:

// 数组无值
id: 2086
intention: 

// 数组有值
id: 2086
intention: 
follower_id[0]: 351
follower_id[1]: 66
concat_material[0][fname]: 视频订单.xls
concat_material[0][key]: local/other/099f4be38fb8e69bb031cbc36ed283a6.xls
concat_material[1][fname]: 视频订单1.xls
concat_material[1][key]: local/other/099f4be38fb8e69bb031cbc36ed283a8.xls

方式三 格式化出来的数据:

// 数组无值
id: 743
intention: 2
follower_id: 
concat_material:

// 数组有值
id: 2086
intention: 
follower_id: 66,351
concat_material: [object Object],[object Object]

参考链接


Vue axios 发送 FormData 请求

解决Uncaught (in promise) Error: Navigation cancelled from “/...“ to “/...“ with a new navigation.

解决

Uncaught (in promise) Error: Navigation cancelled from “/Search#1608911018888” to “/Search#1608911019245” with a new navigation.

这个错误是vue-router内部错误,没有进行catch处理,导致的编程式导航跳转问题,往同一地址跳转,或者在跳转的 mounted/activated 等函数中再次向其他地址跳转会报错。

pushreplace都会导致这个情况的发生

解决方法为在路由中进行如下配置:

import VueRouter from 'vue-router';
Vue.use(VueRouter);
//解决编程式路由往同一地址跳转时会报错的情况
const rop = VueRouter.prototype.push;
const ror = VueRouter.prototype.replace;
//push
VueRouter.prototype.push = function (location, onResolve, onReject) {
    if (onResolve || onReject) return rop.call(this, location, onResolve, onReject)
    return rop.call(this, location).catch(err => err)
};
//replace
VueRouter.prototype.replace = function (location, onResolve, onReject) {
    if (onResolve || onReject) return ror.call(this, location, onResolve, onReject)
    return ror.call(this, location).catch(err => err)
};


..............................

new Vue({
        el: '#q-app',
        router: router,
});

参考链接


Tab切换以及缓存页面处理的几种方式

前言

相信tab切换对于大家来说都不算陌生,后台管理系统中多会用到。如果不知道的话,可以看一下浏览器上方的标签页切换,大概效果就是这样。

1.如何切换

  1. 使用动态组件,相信大家都能看懂(部分代码省略)

    //通过点击就可以实现两个组件来回切换
    <button @click="changeView">切换view</button>
    <component :is="currentView"></component>
    
    import pageA from "@/views/pageA";
    import pageB from "@/views/pageB";
    
    computed: {
      currentView(){
          return this.viewList[this.index];
      }
    },
     methods: {
      changeView() {
        this.index=(++this.index)%2
      }
    }

    注:这个多用于单页下的几个子模块使用,一般切换比较多使用下面的路由

  2. 使用路由(这个就是配置路由的问题了,不作赘述)

2.动态生成tab

一般UI框架给我们的tab切换都像是上面的那种,需要自己写入几个tab页之类的配置。但是我们如果想要通过点击左边的目录来生成一个tab页并且可以随时关闭呢(如下图)?

只需要给路由一个点击事件,把你的路由地址保存到一个列表,渲染成另一个平铺的tab目录即可

假设你的布局是这样,左边的目录,上边的tab,有字的是页面

<menu>
  <menu-item v-for="(item,index) in menuList" :key="index" @click="addToTabList(item.path)">
    <router-link :to="item.path">{{item.name}}</router-link>
  <menu-item>
</menu>
<template>
  <menu class="left"/>//menu代码部分如上
  <div class="right">
    <tab-list>
      <tab-item v-for="(item,index) in tabList" :key="index">
        <router-link :to="item.path">{{item.name}}</router-link>
        <icon class="delete" @click="deleteTab"></icon>
      </tab-item>
    </tab-list>
    <page-view>
      <router-view></router-view>//这里是页面展示
    </page-view>
  </div>
</template>

以上代码并非实际代码,只提供一个大概的思路。至于addToTabListdeleteTab怎么做就是数组方法的简单pushsplice操作了。为了效果好看,我们可能还需要一些tabactive样式,这里不作演示。

3.缓存组件

仅仅是做tab切换,远远是不够的,毕竟大家想要tab页就是要来回切换操作,我们需要保存他在不同tab里操作的进度,比如说填写的表单信息,或者已经查询好的数据列表等。
那么我们要怎么缓存组件呢?
只需要用到vue中的keep-alive组件

3.1 keep-alive

  • <keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
  • <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
  • <keep-alive> 与 <transition>相似,只是一个抽象组件,它不会在DOM树中渲染(真实或者虚拟都不会),也不在父组件链中存在,比如:你永远在 this.$parent 中找不到 keep-alive 。

注:不能使用keep-alive来缓存固定组件,会无效

//无效
<keep-alive>
  <my-component></my-component>
</keep-alive>

3.2 使用

3.2.1 老版本vue 2.1之前的使用

<keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

需要在路由信息里面设置router的元信息meta

export default new Router({
  routes: [
    {
      path: '/a',
      name: 'A',
      component: A,
      meta: {
        keepAlive: false // 不需要缓存
      }
    },
    {
      path: '/b',
      name: 'B',
      component: B,
      meta: {
        keepAlive: true // 需要被缓存
      }
    }
  ]
})

3.2.2 比较新而且简单的用法

  • 直接缓存所有组件/路由
<keep-alive>
    <router-view/>
</keep-alive>
<keep-alive>
   <component :is="view"></component>
</keep-alive>
  • 使用include来处理需要缓存的组件/路由

include有几种用法,可以是数组,字符串用标点隔开,也可以是正则,使用正则的时候需要使用v-bind来绑定。

<keep-alive include="['a','b']">//缓存name为a,b的组件
<keep-alive include ="a,b">//缓存name为a,b的组件
<keep-alive :include="/^store/">//缓存name以store开头的组件
    <router-view/>//可以为router-view
    <component :is="view"></component>//也可以是动态组件
</keep-alive>
  • 使用exclude来排除不需要缓存的路由

include正好相反,在exclude里的组件不会被缓存。用法类似,不作赘述

3.2.3 一种比较奇怪的情况

当页面跳转方式有A->CB->C两种,但是我们从A到C的时候,不需要缓存,从B到C的时候需要缓存。这时候就要用到路由的钩子结合老版本用法来实现了。

export default {
  data() {
    return {};
  },
  methods: {},
  beforeRouteLeave(to, from, next) {
    to.meta.keepAlive = false; // 让下一页不缓存
    next();
  }
};
export default {
  data() {
    return {};
  },
  methods: {},
  beforeRouteLeave(to, from, next) {
    // 设置下一个路由的 meta
    to.meta.keepAlive = true; //下一页缓存
    next();
  }
};

3.3 缓存组件的生命周期函数

缓存组件第一次打开的时候,和普通组件一样,也需要执行createdmounted等函数。
但是在被再次激活被停用时,这几个普通组件的生命周期函数都不会执行,会执行两个比较独特的生命周期函数。

  • activated
    这个会在缓存的组件重新激活时调用
  • deactivated
    这个会在缓存的组件停用时调用

参考链接


JavaScript正则表达式匹配成对出现的标记(平衡组-balanced group)

XRegExp.matchRecursive(str, left, right, [flags], [options])

Requires the XRegExp.matchRecursive addon, which is bundled in `xregexp-all.js`.

Returns an array of match strings between outermost left and right delimiters, or an array of objects with detailed match parts and position data. An error is thrown if delimiters are unbalanced within the data.

Parameters:
  • `str` {`String`}
    String to search.
  • `left` {`String`}
    Left delimiter as an XRegExp pattern.
  • `right` {`String`}
    Right delimiter as an XRegExp pattern.
  • [`flags`] {`String`}
    Any combination of XRegExp flags, used for the left and right delimiters.
  • [`options`] {`Object`}
    Lets you specify `valueNames` and `escapeChar` options.
Returns:
  • {`Array`}
    Array of matches, or an empty array.

Example

// Basic usage
let str = '(t((e))s)t()(ing)';
XRegExp.matchRecursive(str, '\\(', '\\)', 'g');
// -> ['t((e))s', '', 'ing']

// Extended information mode with valueNames
str = 'Here is <div> <div>an</div></div> example';
XRegExp.matchRecursive(str, '<div\\s*>', '</div>', 'gi', {
  valueNames: ['between', 'left', 'match', 'right']
});
/* -> [
{name: 'between', value: 'Here is ',       start: 0,  end: 8},
{name: 'left',    value: '<div>',          start: 8,  end: 13},
{name: 'match',   value: ' <div>an</div>', start: 13, end: 27},
{name: 'right',   value: '</div>',         start: 27, end: 33},
{name: 'between', value: ' example',       start: 33, end: 41}
] */

// Omitting unneeded parts with null valueNames, and using escapeChar
str = '...{1}.\\{{function(x,y){return {y:x}}}';
XRegExp.matchRecursive(str, '{', '}', 'g', {
  valueNames: ['literal', null, 'value', null],
  escapeChar: '\\'
});
/* -> [
{name: 'literal', value: '...',  start: 0, end: 3},
{name: 'value',   value: '1',    start: 4, end: 5},
{name: 'literal', value: '.\\{', start: 6, end: 9},
{name: 'value',   value: 'function(x,y){return {y:x}}', start: 10, end: 37}
] */

// Sticky mode via flag y
str = '<1><<<2>>><3>4<5>';
XRegExp.matchRecursive(str, '<', '>', 'gy');
// -> ['1', '<<2>>', '3']

参考链接


vue中router-view与父组件之间的通信

在项目当中,遇到一个问题。当父页面的某个属性变化时,需要router-view中的页面根据不同的值进行不同的操作。

仔细想一下,其实类似父子组件之间的传值。

实现过程如下:

  1. 父组件绑定属性和事件
<template>
   <router-view v-on:test="testP" v-bind:msg="msg"></router-view>
</template>
<script type="text/javascript">
   export default {
      data() {
         return {
            msg: "把我带给router-view吧!"
         }
      },
      methods: {
        testP: function (data) {
          // 从router-view返回来的数据
          console.log(data)
          // 打印出来就是
          // 把我带给父组件吧!第一次!
          // 把我带给父组件吧!第二次!
        }
      }
   }
</script>
  1. router-view关联的属性和监听动作
<template>
   <div>{{msg}}</div>
</template>
<script type="text/javascript">
   export default {
      props:['msg'],
      data() {
         return {}
      },
      watch: {
        // 监听父组件的msg的变化
        msg: function() {
          console.log(this.msg)
          // 打印出来就是
          // 把我带给router-view吧!
        }
      },
      mounted() {
        // this.init()
      },
      methods: {
        init() {
          // 第一次向父组件传值
          this.$emit("test", "把我带给父组件吧!第一次!")
          // 第二次向父组件传值
          this.$emit("test", "把我带给父组件吧!第二次!")
        }
      }
   }

注意:

<router-view v-on:test="testP" v-bind:msg="msg"></router-view>

v-on 绑定的函数名 `test`,尽量不要出现大写字母(驼峰命名)(比如 v-on:Aplus_clicked="testP"),否则在某些特殊使用方式的情况下,可能会出现无法触发的问题

驼峰命名,可能会发生如下报错:

[Vue tip]: Event "aplus_clicked" is emitted in component <Anonymous> but the handler is registered for "Aplus_clicked". Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use "aplus_clicked" instead of "Aplus_clicked".

参考链接


Evaluating JavaScript code via import()

The `import()` operator lets us dynamically load ECMAScript modules. But they can also be used to evaluate JavaScript code (as Andrea Giammarchi recently pointed out to me), as an alternative to `eval()`. This blog post explains how that works.

`eval()` does not support `export` and `import`

A significant limitation of `eval()` is that it doesn’t support module syntax such as `export` and `import`.

If we use `import()` instead of `eval()`, we can actually evaluate module code, as we will see later in this blog post.

In the future, we may get Realms which are, roughly, a more powerful `eval()` with support for modules.

Evaluating simple code via `import()`

Let’s start by evaluating a `console.log()` via `import()`:

const js = `console.log('Hello everyone!');`;
const encodedJs = encodeURIComponent(js);
const dataUri = 'data:text/javascript;charset=utf-8,'
  + encodedJs;
import(dataUri);

// Output:
// 'Hello everyone!'

What is going on here?

  • First we create a so-called data URI. The protocol of this kind of URI is `data:`. The remainder of the URI encodes the full resource instead pointing to it. In this case, the data URI contains a complete ECMAScript module – whose content type is `text/javascript`.
  • Then we dynamically import this module and therefore execute it.

Warning: This code only works in web browsers. On Node.js, `import()` does not support data URIs.

Accessing an export of an evaluated module  

The fulfillment value of the Promise returned by `import()` is a module namespace object. That gives us access to the default export and the named exports of the module. In the following example, we access the default export:

const js = `export default 'Returned value'`;
const dataUri = 'data:text/javascript;charset=utf-8,'
  + encodeURIComponent(js);
import(dataUri)
  .then((namespaceObject) => {
    assert.equal(namespaceObject.default, 'Returned value');
  })
  .catch((err) => {
     console.log(err.message); // "Importing a module script failed."
     // apply some logic, e.g. show a feedback for the user
   });

Creating data URIs via tagged templates

With an appropriate function `esm` (whose implementation we’ll see later), we can rewrite the previous example and create the data URI via a tagged template:

const dataUri = esm`export default 'Returned value'`;
import(dataUri)
  .then((namespaceObject) => {
    assert.equal(namespaceObject.default, 'Returned value');
  })
  .catch((err) => {
     console.log(err.message); // "Importing a module script failed."
     // apply some logic, e.g. show a feedback for the user
   });

The implementation of `esm` looks as follows:

function esm(templateStrings, ...substitutions) {
  let js = templateStrings.raw[0];
  for (let i=0; i<substitutions.length; i++) {
    js += substitutions[i] + templateStrings.raw[i+1];
  }
  return 'da

For the encoding, we have switched from `charset=utf-8` to `base64`. Compare:

  • Source code: `'a' < 'b'`
  • Data URI 1: `data:text/javascript;charset=utf-8,'a'%20%3C%20'b'`
  • Data URI 2: `data:text/javascript;base64,J2EnIDwgJ2In`

Each of the two ways of encoding has different pros and cons:

  • Benefits of charset=utf-8 (percent-encoding):
    • Much of the source code is still readable.
  • Benefits of base64:
    • The URIs are usually shorter.
    • Easier to nest because it doesn’t contain special characters such as apostrophes. We’ll see an example of nesting in the next section.

`btoa()` is a global utility function that encodes a string via base 64. Caveats:

  • It is not available on Node.js.
  • It should only be used for characters whose Unicode code points range from 0 to 255.

Evaluating a module that imports another module  

With tagged templates, we can nest data URIs and encode a module `m2` that imports another module `m1`:

const m1 = esm`export function f() { return 'Hello!' }`;
const m2 = esm`import {f} from '${m1}'; export default f()+f();`;
import(m2)
  .then(ns => assert.equal(ns.default, 'Hello!Hello!'))
  .catch((err) => {
     console.log(err.message); // "Importing a module script failed."
     // apply some logic, e.g. show a feedback for the user
   });

Further reading  

参考链接


判断网页是通过PC端还是移动终端打开的

通过判断打开设备,跳转不同页面,可以根据 User-Agent 来区分
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
    window.location.href = "";     //手机
} else {
    window.location.href = "";        //电脑
}

也可以执行其他操作:

if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
    alert('您正在通过手机访问');
} else {
    alert("您在PC端访问");
}

JS判断客户端是否是iOS或者Android手机移动端:

通过判断浏览器的 userAgent,用正则来判断手机是否是ios和Android客户端。代码如下:

<script type="text/javascript">
    var u = navigator.userAgent;
    var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
    var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
    alert('是否是Android:'+isAndroid);
    alert('是否是iOS:'+isiOS);
</script>

下面一个比较全面的浏览器检查函数,提供更多的检查内容,你可以检查是否是移动端(Mobile)、ipad、iphone、微信、QQ等。

第一种:

<script type="text/javascript">
    //判断访问终端
    var browser={
        versions:function(){
            var u = navigator.userAgent, app = navigator.appVersion;
            return {
                trident: u.indexOf('Trident') > -1, //IE内核
                presto: u.indexOf('Presto') > -1, //opera内核
                webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
                gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1,//火狐内核
                mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
                ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
                android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, //android终端
                iPhone: u.indexOf('iPhone') > -1 , //是否为iPhone或者QQHD浏览器
                iPad: u.indexOf('iPad') > -1, //是否iPad
                webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部
                weixin: u.indexOf('MicroMessenger') > -1, //是否微信 (2015-01-22新增)
                qq: u.match(/\sQQ/i) == " qq" //是否QQ
            };
        }(),
        language:(navigator.browserLanguage || navigator.language).toLowerCase()
    }
    //使用方法:
    //判断是否IE内核
    if(browser.versions.trident){ alert("is IE"); }
    //判断是否webKit内核
    if(browser.versions.webKit){ alert("is webKit"); }
    //判断是否移动端
    if(browser.versions.mobile||browser.versions.android||browser.versions.ios){
        alert("移动端"); 
    }
</script>

 

检测浏览器语言

currentLang = navigator.language;   //判断除IE外其他浏览器使用语言
if(!currentLang){//判断IE浏览器使用语言
    currentLang = navigator.browserLanguage;
}
alert(currentLang);

第二种:

if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
    //alert(navigator.userAgent);  
    window.location.href ="iPhone.html";
} else if (/(Android)/i.test(navigator.userAgent)) {
    //alert(navigator.userAgent); 
    window.location.href ="Android.html";
} else {
    window.location.href ="pc.html";
};

也可以通过这样来适配,然后直接转跳到移动端页面:

function mobile_device_detect(url){
  var thisOS=navigator.platform;
  var os=new Array("iPhone","iPod","iPad","android","Nokia","SymbianOS","Symbian","Windows Phone","Phone","Linux armv71","MAUI","UNTRUSTED/1.0","Windows CE","BlackBerry","IEMobile");
  for(var i=0;i<os.length;i++){
    if(thisOS.match(os[i])){
      window.location.href=url;
    }
  }
  if(navigator.platform.indexOf('iPad') != -1){
    window.location.href=url;
  }
  var check = navigator.appVersion;
  if( check.match(/linux/i) ){
    if(check.match(/mobile/i) || check.match(/X11/i)){
      window.location.href=url;
    }
  }
}

参考链接


判断网页是通过PC端还是移动终端打开的

window.onerror的总结

最近一直在做前端js错误监控的工作,在不断的打磨和完善中,发现里面还是知识点不少,现在就前端js错误监控做一些笔记和总结

我们知道前端js错误监控主要是利用了window.onerror函数来实现,onerror函数会在页面发生js错误时被调用。

window.onerror = function(message, source, lineno, colno, error) { ... }

我们可以看到函数正常是可以收集到错误字符串信息、发生错误的js文件,错误所在的行数、列数、和Error对象(里面会有调用堆栈信息等)。

我们只需要把这些信息回传到server端即可,再配合sourcemap的话我们就可以知道是源码中的哪一行出错了,从而实现完美的错误实时监控系统了。然而要完美还是需要做很多工作的。

继续阅读window.onerror的总结