From 7bde77965ae2370ca1cd846415b87cd5b2dbe934 Mon Sep 17 00:00:00 2001 From: minlingchao <738509878@qq.com> Date: Thu, 13 Jul 2017 23:47:48 +0800 Subject: [PATCH] add code --- .babelrc | 18 ++++ .editorconfig | 9 ++ .eslintignore | 2 + .eslintrc.js | 27 ++++++ .gitignore | 14 +++ .npmignore | 14 +++ .npminstall.done | 1 + .postcssrc.js | 8 ++ README.md | 102 +++++++++++++++++++++ build/build.js | 35 +++++++ build/check-versions.js | 48 ++++++++++ build/dev-client.js | 9 ++ build/dev-server.js | 91 ++++++++++++++++++ build/utils.js | 71 ++++++++++++++ build/vue-loader.conf.js | 12 +++ build/webpack.base.conf.js | 41 +++++++++ build/webpack.dev.conf.js | 35 +++++++ build/webpack.prod.conf.js | 61 ++++++++++++ config/dev.env.js | 6 ++ config/index.js | 38 ++++++++ config/prod.env.js | 3 + config/test.env.js | 6 ++ dist/index.js | 2 + dist/index.js.map | 1 + index.html | 11 +++ package.json | 84 +++++++++++++++++ src/components/ba.js | 27 ++++++ src/components/ba.vue | 7 ++ src/directives/auto-pageview.js | 10 ++ src/directives/track-event.js | 32 +++++++ src/directives/track-pageview.js | 38 ++++++++ src/directives/utils.js | 23 +++++ src/index.js | 82 +++++++++++++++++ src/install.js | 57 ++++++++++++ static/.gitkeep | 0 test/e2e/custom-assertions/elementCount.js | 26 ++++++ test/e2e/nightwatch.conf.js | 46 ++++++++++ test/e2e/runner.js | 33 +++++++ test/e2e/specs/test.js | 19 ++++ 39 files changed, 1149 insertions(+) create mode 100644 .babelrc create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .npminstall.done create mode 100644 .postcssrc.js create mode 100644 README.md create mode 100644 build/build.js create mode 100644 build/check-versions.js create mode 100644 build/dev-client.js create mode 100644 build/dev-server.js create mode 100644 build/utils.js create mode 100644 build/vue-loader.conf.js create mode 100644 build/webpack.base.conf.js create mode 100644 build/webpack.dev.conf.js create mode 100644 build/webpack.prod.conf.js create mode 100644 config/dev.env.js create mode 100644 config/index.js create mode 100644 config/prod.env.js create mode 100644 config/test.env.js create mode 100644 dist/index.js create mode 100644 dist/index.js.map create mode 100644 index.html create mode 100644 package.json create mode 100644 src/components/ba.js create mode 100644 src/components/ba.vue create mode 100644 src/directives/auto-pageview.js create mode 100644 src/directives/track-event.js create mode 100644 src/directives/track-pageview.js create mode 100644 src/directives/utils.js create mode 100644 src/index.js create mode 100644 src/install.js create mode 100644 static/.gitkeep create mode 100644 test/e2e/custom-assertions/elementCount.js create mode 100644 test/e2e/nightwatch.conf.js create mode 100644 test/e2e/runner.js create mode 100644 test/e2e/specs/test.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..c06df4d --- /dev/null +++ b/.babelrc @@ -0,0 +1,18 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-runtime"], + "env": { + "test": { + "presets": ["env", "stage-2"], + "plugins": ["istanbul"] + } + } +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9d08a1a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..34af377 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +build/*.js +config/*.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..67c085d --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,27 @@ +// http://eslint.org/docs/user-guide/configuring + +module.exports = { + root: true, + parser: 'babel-eslint', + parserOptions: { + sourceType: 'module' + }, + env: { + browser: true, + }, + // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style + extends: 'standard', + // required to lint *.vue files + plugins: [ + 'html' + ], + // add your custom rules here + 'rules': { + // allow paren-less arrow functions + 'arrow-parens': 0, + // allow async-await + 'generator-star-spacing': 0, + // allow debugger during development + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a59616b --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +test/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..a59616b --- /dev/null +++ b/.npmignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +test/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/.npminstall.done b/.npminstall.done new file mode 100644 index 0000000..2a6cf1d --- /dev/null +++ b/.npminstall.done @@ -0,0 +1 @@ +Thu Jul 13 2017 19:55:37 GMT+0800 (CST) \ No newline at end of file diff --git a/.postcssrc.js b/.postcssrc.js new file mode 100644 index 0000000..ea9a5ab --- /dev/null +++ b/.postcssrc.js @@ -0,0 +1,8 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + // to edit target browsers: use "browserlist" field in package.json + "autoprefixer": {} + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..a78736c --- /dev/null +++ b/README.md @@ -0,0 +1,102 @@ +--- +title: VueJs百度统计插件 +date: 2017-07-13 23:10:31 +tags: + - vue + - 百度统计 + +--- + +# vue-ba + +> vuejs 百度统计埋点插件 +> +> 参考项目:https://github.com/raychenfj/vue-uweb + +## 1. 安装 + +``` +npm install vue-ba --save + +``` +直接在页面中引用 +``` + diff --git a/src/directives/auto-pageview.js b/src/directives/auto-pageview.js new file mode 100644 index 0000000..a49d85f --- /dev/null +++ b/src/directives/auto-pageview.js @@ -0,0 +1,10 @@ +import ba from '../index' +import { notChanged } from './utils' +export default function (el, binding) { + if (notChanged(binding)) return + + let args = [] + if (binding.value === false || binding.value === 'false') args.push(false) + else args.push(true) + ba.setAutoPageview(...args) +} diff --git a/src/directives/track-event.js b/src/directives/track-event.js new file mode 100644 index 0000000..6a9a346 --- /dev/null +++ b/src/directives/track-event.js @@ -0,0 +1,32 @@ +import ba from '../index' +import { notChanged, isEmpty } from './utils' + +export default function (el, binding) { + if (notChanged(binding) || isEmpty(binding)) { + return + } + + let args = [] + let events = Object.keys(binding.modifiers).map(modifier => { + if (binding.modifiers[modifier]) { + return modifier + } + }) + + if (typeof binding.value === 'object') { + let value = binding.value + if (value.category) args.push(value.category) + if (value.action) args.push(value.action) + if (value.opt_label) args.push(value.opt_label) + if (value.opt_value) args.push(value.opt_value) + } else if (typeof binding.value === 'string') { + args = binding.value.split(',') + args.forEach((arg, i) => (args[i] = arg.trim())) + } + + if (!events.length) events.push('click') // default listen click + + events.forEach((event) => { + el.addEventListener(event, () => ba.trackEvent(...args), false) + }) +} diff --git a/src/directives/track-pageview.js b/src/directives/track-pageview.js new file mode 100644 index 0000000..9da8a7a --- /dev/null +++ b/src/directives/track-pageview.js @@ -0,0 +1,38 @@ +import ba from '../index' +import { notChanged, isEmpty } from './utils' + +export let watch = [] + +const trackPageview = { + bind (el, binding) { + let index = watch.findIndex(element => element === el) + let isWatched = index !== -1 + if (el.style.display === 'none') { + if (!isWatched) watch.push(el) + return + } else { + if (isWatched) watch.splice(index, 1) + } + + if (!isWatched && (notChanged(binding) || isEmpty(binding))) return + + let args = [] + + if (typeof binding.value === 'object') { + let value = binding.value + if (value.pageURL) args.push(value.pageURL) + } else if (typeof binding.value === 'string' && binding.value) { + args = binding.value.split(',') + args.forEach((arg, i) => (arg[i] = arg.trim())) + } + ba.trackPageview(...args) + }, + unbind (el, binding) { + let index = watch.findIndex(element => element === el) + if (index !== -1) watch.splice(index, 1) + } +} + +trackPageview.update = trackPageview.bind + +export default trackPageview diff --git a/src/directives/utils.js b/src/directives/utils.js new file mode 100644 index 0000000..116be8e --- /dev/null +++ b/src/directives/utils.js @@ -0,0 +1,23 @@ +import deepEqual from 'deep-equal' + +/** + * if the binding value is equal to oldeValue + */ +export function notChanged (binding) { + if (binding.oldValue !== undefined) { + if (typeof binding.value === 'object') { + return deepEqual(binding.value, binding.oldValue) + } else { + return binding.value === binding.oldValue + } + } else { + return false + } +} + +/** + * if the binding value is empty + */ +export function isEmpty (binding) { + return binding.value === '' || binding.value === undefined || binding.value === null +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..7fa70ff --- /dev/null +++ b/src/index.js @@ -0,0 +1,82 @@ +import install from './install' + +const deferred = {} +deferred.promise = new Promise((resolve, reject) => { + deferred.resolve = resolve + deferred.reject = reject +}) +const methods = [ + 'trackPageview', + 'trackEvent', + 'setCustomVar' +] + +const ba = { + /** + * internal user only + */ + _cache: [], + /** + * internal user only, resolve the promise + */ + _resolve () { + deferred.resolve() + }, + /** + * internal user only, reject the promise + */ + _reject () { + deferred.reject() + }, + + /** + * push the args into _czc, or _cache if the script is not loaded yet + */ + _push (...args) { + this.debug(args) + if (window._hmt) { + window._hmt.push(...args) + } else { + this._cache.push(...args) + } + }, + /** + * general method to create baidu analystics apis + */ + _createMethod (method) { + return (...args) => { + this._push([`_${method}`, ...args]) + } + }, + + /** + * debug + */ + debug () {}, + /** + * the plugins is ready when the script is loaded + */ + ready () { + return deferred.promise + }, + /** + * install function + */ + + install, + /** + * patch up to create new api + */ + patch (method) { + this[method] = this._createMethod(method) + } +} + +// uweb apis +methods.forEach((method) => (ba[method] = ba._createMethod(method))) + +if (window.Vue) { + window.ba = ba +} + +export default ba diff --git a/src/install.js b/src/install.js new file mode 100644 index 0000000..f1c4c5f --- /dev/null +++ b/src/install.js @@ -0,0 +1,57 @@ +import autoPageview from './directives/auto-pageview' +import trackEvent from '././directives/track-event' +import trackPageview from '././directives/track-pageview' +export default function install (Vue, options) { + if (this.install.installed) return + + if (options.debug) { + this.debug = console.log + } else { + this.debug = () => {} + } + + let siteId = null + + if (typeof options === 'object') { + siteId = options.siteId + if (options.autoPageview !== false) { + options.autoPageview = true + } + } else { + siteId = options + } + + if (!siteId) { + return console.error(' siteId is missing') + } + + this.install.installed = true + // insert baidu analystics scripts + const script = document.createElement('script') + const src = `https://hm.baidu.com/hm.js?` + siteId + const realSrc = options.src || src + script.innerHTML = 'var _hmt = _hmt || []; (function(){var hm = document.createElement(\'script\');hm.src="' + + realSrc + + '";var s = document.getElementsByTagName("script")[0];s.parentNode.insertBefore(hm, s);})()' + script.onload = () => { + // if the global object is exist, resolve the promise, otherwise reject it + if (window._hmt) { + this._resolve() + } else { + console.error('loading ba statistics script failed, please check src and siteId') + return this._reject() + } + this._cache.forEach((cache) => { + window._hmt.push(cache) + }) + this._cache = [] + } + document.body.appendChild(script) + Object.defineProperty(Vue.prototype, '$ba', { + get: () => this + }) + + Vue.directive('auto-pageview', autoPageview) + Vue.directive('track-event', trackEvent) + Vue.directive('track-pageview', trackPageview) +} diff --git a/static/.gitkeep b/static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/e2e/custom-assertions/elementCount.js b/test/e2e/custom-assertions/elementCount.js new file mode 100644 index 0000000..c0d5fe0 --- /dev/null +++ b/test/e2e/custom-assertions/elementCount.js @@ -0,0 +1,26 @@ +// A custom Nightwatch assertion. +// the name of the method is the filename. +// can be used in tests like this: +// +// browser.assert.elementCount(selector, count) +// +// for how to write custom assertions see +// http://nightwatchjs.org/guide#writing-custom-assertions +exports.assertion = function (selector, count) { + this.message = 'Testing if element <' + selector + '> has count: ' + count + this.expected = count + this.pass = function (val) { + return val === this.expected + } + this.value = function (res) { + return res.value + } + this.command = function (cb) { + var self = this + return this.api.execute(function (selector) { + return document.querySelectorAll(selector).length + }, [selector], function (res) { + cb.call(self, res) + }) + } +} diff --git a/test/e2e/nightwatch.conf.js b/test/e2e/nightwatch.conf.js new file mode 100644 index 0000000..f019c0a --- /dev/null +++ b/test/e2e/nightwatch.conf.js @@ -0,0 +1,46 @@ +require('babel-register') +var config = require('../../config') + +// http://nightwatchjs.org/gettingstarted#settings-file +module.exports = { + src_folders: ['test/e2e/specs'], + output_folder: 'test/e2e/reports', + custom_assertions_path: ['test/e2e/custom-assertions'], + + selenium: { + start_process: true, + server_path: require('selenium-server').path, + host: '127.0.0.1', + port: 4444, + cli_args: { + 'webdriver.chrome.driver': require('chromedriver').path + } + }, + + test_settings: { + default: { + selenium_port: 4444, + selenium_host: 'localhost', + silent: true, + globals: { + devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) + } + }, + + chrome: { + desiredCapabilities: { + browserName: 'chrome', + javascriptEnabled: true, + acceptSslCerts: true + } + }, + + firefox: { + desiredCapabilities: { + browserName: 'firefox', + javascriptEnabled: true, + acceptSslCerts: true + } + } + } +} diff --git a/test/e2e/runner.js b/test/e2e/runner.js new file mode 100644 index 0000000..85d67d6 --- /dev/null +++ b/test/e2e/runner.js @@ -0,0 +1,33 @@ +// 1. start the dev server using production config +process.env.NODE_ENV = 'testing' +var server = require('../../build/dev-server.js') + +server.ready.then(() => { + // 2. run the nightwatch test suite against it + // to run in additional browsers: + // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings" + // 2. add it to the --env flag below + // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` + // For more information on Nightwatch's config file, see + // http://nightwatchjs.org/guide#settings-file + var opts = process.argv.slice(2) + if (opts.indexOf('--config') === -1) { + opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) + } + if (opts.indexOf('--env') === -1) { + opts = opts.concat(['--env', 'chrome']) + } + + var spawn = require('cross-spawn') + var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) + + runner.on('exit', function (code) { + server.close() + process.exit(code) + }) + + runner.on('error', function (err) { + server.close() + throw err + }) +}) diff --git a/test/e2e/specs/test.js b/test/e2e/specs/test.js new file mode 100644 index 0000000..a7b1bd9 --- /dev/null +++ b/test/e2e/specs/test.js @@ -0,0 +1,19 @@ +// For authoring Nightwatch tests, see +// http://nightwatchjs.org/guide#usage + +module.exports = { + 'default e2e tests': function (browser) { + // automatically uses dev Server port from /config.index.js + // default: http://localhost:8080 + // see nightwatch.conf.js + const devServer = browser.globals.devServerURL + + browser + .url(devServer) + .waitForElementVisible('#app', 5000) + .assert.elementPresent('.hello') + .assert.containsText('h1', 'Welcome to Your Vue.js App') + .assert.elementCount('img', 1) + .end() + } +}