diff --git a/.flowconfig b/.flowconfig index 566a6e87f..932e75a71 100644 --- a/.flowconfig +++ b/.flowconfig @@ -12,3 +12,4 @@ flow [options] unsafe.enable_getters_and_setters=true +suppress_comment= \\(.\\|\n\\)*\\$flow-disable-line diff --git a/README.md b/README.md index 59506dadc..5cc58a79a 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ npm run docs ## Questions -For questions and support please use the [Gitter chat room](https://gitter.im/vuejs/vue) or [the official forum](http://forum.vuejs.org). The issue list of this repo is **exclusively** for bug reports and feature requests. +For questions and support please use the [Discord chat server](https://chat.vuejs.org) or [the official forum](http://forum.vuejs.org). The issue list of this repo is **exclusively** for bug reports and feature requests. ## Issues @@ -60,6 +60,6 @@ Details changes for each release are documented in the [release notes](https://g [MIT](http://opensource.org/licenses/MIT) -Copyright (c) 2013-2016 Evan You +Copyright (c) 2013-2017 Evan You diff --git a/build/build.js b/build/build.js index 0175de415..7e43ccf59 100644 --- a/build/build.js +++ b/build/build.js @@ -3,46 +3,13 @@ const path = require('path') const zlib = require('zlib') const uglify = require('uglify-js') const rollup = require('rollup') -const buble = require('rollup-plugin-buble') -const flow = require('rollup-plugin-flow-no-whitespace') -const cjs = require('rollup-plugin-commonjs') -const node = require('rollup-plugin-node-resolve') -const replace = require('rollup-plugin-replace') -const version = process.env.VERSION || require('../package.json').version -const banner = -`/** - * vue-router v${version} - * (c) ${new Date().getFullYear()} Evan You - * @license MIT - */` +const configs = require('./configs') if (!fs.existsSync('dist')) { fs.mkdirSync('dist') } -const resolve = _path => path.resolve(__dirname, '../', _path) - -build([ - // browser dev - { - dest: resolve('dist/vue-router.js'), - format: 'umd', - env: 'development' - }, - { - dest: resolve('dist/vue-router.min.js'), - format: 'umd', - env: 'production' - }, - { - dest: resolve('dist/vue-router.common.js'), - format: 'cjs' - }, - { - dest: resolve('dist/vue-router.esm.js'), - format: 'es' - } -].map(genConfig)) +build(Object.keys(configs).map(key => configs[key])) function build (builds) { let built = 0 @@ -59,51 +26,24 @@ function build (builds) { next() } -function genConfig (opts) { - const config = { - entry: resolve('src/index.js'), - dest: opts.dest, - format: opts.format, - banner, - moduleName: 'VueRouter', - plugins: [ - flow(), - node(), - cjs(), - replace({ - __VERSION__: version - }), - buble() - ] - } - - if (opts.env) { - config.plugins.unshift(replace({ - 'process.env.NODE_ENV': JSON.stringify(opts.env) - })) - } - - return config -} - -function buildEntry (config) { - const isProd = /min\.js$/.test(config.dest) - return rollup.rollup(config).then(bundle => { - const code = bundle.generate(config).code - if (isProd) { - var minified = (config.banner ? config.banner + '\n' : '') + uglify.minify(code, { - output: { - ascii_only: true - }, - compress: { - pure_funcs: ['makeMap'] - } - }).code - return write(config.dest, minified, true) - } else { - return write(config.dest, code) - } - }) +function buildEntry ({ input, output }) { + const isProd = /min\.js$/.test(output.file) + return rollup.rollup(input) + .then(bundle => bundle.generate(output)) + .then(({ code }) => { + if (isProd) { + var minified = (output.banner ? output.banner + '\n' : '') + uglify.minify(code, { + output: { + /* eslint-disable camelcase */ + ascii_only: true + /* eslint-enable camelcase */ + } + }).code + return write(output.file, minified, true) + } else { + return write(output.file, code) + } + }) } function write (dest, code, zip) { diff --git a/build/configs.js b/build/configs.js new file mode 100644 index 000000000..8417b77f9 --- /dev/null +++ b/build/configs.js @@ -0,0 +1,68 @@ +const path = require('path') +const buble = require('rollup-plugin-buble') +const flow = require('rollup-plugin-flow-no-whitespace') +const cjs = require('rollup-plugin-commonjs') +const node = require('rollup-plugin-node-resolve') +const replace = require('rollup-plugin-replace') +const version = process.env.VERSION || require('../package.json').version +const banner = +`/** + * vue-router v${version} + * (c) ${new Date().getFullYear()} Evan You + * @license MIT + */` + +const resolve = _path => path.resolve(__dirname, '../', _path) + +module.exports = [ + // browser dev + { + file: resolve('dist/vue-router.js'), + format: 'umd', + env: 'development' + }, + { + file: resolve('dist/vue-router.min.js'), + format: 'umd', + env: 'production' + }, + { + file: resolve('dist/vue-router.common.js'), + format: 'cjs' + }, + { + file: resolve('dist/vue-router.esm.js'), + format: 'es' + } +].map(genConfig) + +function genConfig (opts) { + const config = { + input: { + input: resolve('src/index.js'), + plugins: [ + flow(), + node(), + cjs(), + replace({ + __VERSION__: version + }), + buble() + ] + }, + output: { + file: opts.file, + format: opts.format, + banner, + name: 'VueRouter' + } + } + + if (opts.env) { + config.input.plugins.unshift(replace({ + 'process.env.NODE_ENV': JSON.stringify(opts.env) + })) + } + + return config +} diff --git a/build/dev.config.js b/build/dev.config.js deleted file mode 100644 index e4c35c168..000000000 --- a/build/dev.config.js +++ /dev/null @@ -1,15 +0,0 @@ -const buble = require('rollup-plugin-buble') -const flow = require('rollup-plugin-flow-no-whitespace') -const cjs = require('rollup-plugin-commonjs') -const node = require('rollup-plugin-node-resolve') -const replace = require('rollup-plugin-replace') - -module.exports = { - entry: 'src/index.js', - dest: 'dist/vue-router.js', - format: 'umd', - moduleName: 'VueRouter', - plugins: [replace({ - 'process.env.NODE_ENV': '"development"' - }), flow(), node(), cjs(), buble()] -} diff --git a/build/rollup.dev.config.js b/build/rollup.dev.config.js new file mode 100644 index 000000000..996fc2042 --- /dev/null +++ b/build/rollup.dev.config.js @@ -0,0 +1,3 @@ +const { input, output } = require('./configs')[0] + +module.exports = Object.assign({}, input, { output }) diff --git a/dist/vue-router.common.js b/dist/vue-router.common.js index 22b042c79..42cf1366e 100644 --- a/dist/vue-router.common.js +++ b/dist/vue-router.common.js @@ -1,5 +1,5 @@ /** - * vue-router v2.7.0 + * vue-router v2.8.0 * (c) 2017 Evan You * @license MIT */ @@ -89,7 +89,7 @@ var View = { } } - // also regiseter instance in prepatch hook + // also register instance in prepatch hook // in case the same component instance is reused across different routes ;(data.hook || (data.hook = {})).prepatch = function (_, vnode) { matched.instances[name] = vnode.componentInstance; @@ -97,6 +97,14 @@ var View = { // resolve props data.props = resolveProps(route, matched.props && matched.props[name]); + data.attrs = {}; + + for (var key in data.props) { + if (!('props' in component) || !(key in component.props)) { + data.attrs[key] = data.props[key]; + delete data.props[key]; + } + } return h(component, data, children) } @@ -154,8 +162,7 @@ function resolveQuery ( parsedQuery = {}; } for (var key in extraQuery) { - var val = extraQuery[key]; - parsedQuery[key] = Array.isArray(val) ? val.slice() : val; + parsedQuery[key] = extraQuery[key]; } return parsedQuery } @@ -232,12 +239,18 @@ function createRoute ( router ) { var stringifyQuery$$1 = router && router.options.stringifyQuery; + + var query = location.query || {}; + try { + query = clone(query); + } catch (e) {} + var route = { name: location.name || (record && record.name), meta: (record && record.meta) || {}, path: location.path || '/', hash: location.hash || '', - query: location.query || {}, + query: query, params: location.params || {}, fullPath: getFullPath(location, stringifyQuery$$1), matched: record ? formatMatch(record) : [] @@ -248,6 +261,20 @@ function createRoute ( return Object.freeze(route) } +function clone (value) { + if (Array.isArray(value)) { + return value.map(clone) + } else if (value && typeof value === 'object') { + var res = {}; + for (var key in value) { + res[key] = clone(value[key]); + } + return res + } else { + return value + } +} + // the starting route that represents the initial state var START = createRoute(null, { path: '/' @@ -301,6 +328,8 @@ function isObjectEqual (a, b) { if ( a === void 0 ) a = {}; if ( b === void 0 ) b = {}; + // handle null value #1566 + if (!a || !b) { return a === b } var aKeys = Object.keys(a); var bKeys = Object.keys(b); if (aKeys.length !== bKeys.length) { @@ -480,7 +509,7 @@ function findAnchor (children) { var _Vue; function install (Vue) { - if (install.installed) { return } + if (install.installed && _Vue === Vue) { return } install.installed = true; _Vue = Vue; @@ -602,14 +631,14 @@ function cleanPath (path) { return path.replace(/\/\//g, '/') } -var index$1 = Array.isArray || function (arr) { +var isarray = Array.isArray || function (arr) { return Object.prototype.toString.call(arr) == '[object Array]'; }; /** * Expose `pathToRegexp`. */ -var index = pathToRegexp; +var pathToRegexp_1 = pathToRegexp; var parse_1 = parse; var compile_1 = compile; var tokensToFunction_1 = tokensToFunction; @@ -786,7 +815,7 @@ function tokensToFunction (tokens) { } } - if (index$1(value)) { + if (isarray(value)) { if (!token.repeat) { throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`') } @@ -937,7 +966,7 @@ function stringToRegexp (path, keys, options) { * @return {!RegExp} */ function tokensToRegExp (tokens, keys, options) { - if (!index$1(keys)) { + if (!isarray(keys)) { options = /** @type {!Object} */ (keys || options); keys = []; } @@ -1013,7 +1042,7 @@ function tokensToRegExp (tokens, keys, options) { * @return {!RegExp} */ function pathToRegexp (path, keys, options) { - if (!index$1(keys)) { + if (!isarray(keys)) { options = /** @type {!Object} */ (keys || options); keys = []; } @@ -1024,20 +1053,21 @@ function pathToRegexp (path, keys, options) { return regexpToRegexp(path, /** @type {!Array} */ (keys)) } - if (index$1(path)) { + if (isarray(path)) { return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options) } return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options) } -index.parse = parse_1; -index.compile = compile_1; -index.tokensToFunction = tokensToFunction_1; -index.tokensToRegExp = tokensToRegExp_1; +pathToRegexp_1.parse = parse_1; +pathToRegexp_1.compile = compile_1; +pathToRegexp_1.tokensToFunction = tokensToFunction_1; +pathToRegexp_1.tokensToRegExp = tokensToRegExp_1; /* */ +// $flow-disable-line var regexpCompileCache = Object.create(null); function fillParams ( @@ -1048,7 +1078,7 @@ function fillParams ( try { var filler = regexpCompileCache[path] || - (regexpCompileCache[path] = index.compile(path)); + (regexpCompileCache[path] = pathToRegexp_1.compile(path)); return filler(params || {}, { pretty: true }) } catch (e) { if (process.env.NODE_ENV !== 'production') { @@ -1068,7 +1098,9 @@ function createRouteMap ( ) { // the path list is used to control path matching priority var pathList = oldPathList || []; + // $flow-disable-line var pathMap = oldPathMap || Object.create(null); + // $flow-disable-line var nameMap = oldNameMap || Object.create(null); routes.forEach(function (route) { @@ -1110,8 +1142,12 @@ function addRouteRecord ( ); } - var normalizedPath = normalizePath(path, parent); var pathToRegexpOptions = route.pathToRegexpOptions || {}; + var normalizedPath = normalizePath( + path, + parent, + pathToRegexpOptions.strict + ); if (typeof route.caseSensitive === 'boolean') { pathToRegexpOptions.sensitive = route.caseSensitive; @@ -1199,9 +1235,9 @@ function addRouteRecord ( } function compileRouteRegex (path, pathToRegexpOptions) { - var regex = index(path, [], pathToRegexpOptions); + var regex = pathToRegexp_1(path, [], pathToRegexpOptions); if (process.env.NODE_ENV !== 'production') { - var keys = {}; + var keys = Object.create(null); regex.keys.forEach(function (key) { warn(!keys[key.name], ("Duplicate param keys in route with path: \"" + path + "\"")); keys[key.name] = true; @@ -1210,8 +1246,8 @@ function compileRouteRegex (path, pathToRegexpOptions) { return regex } -function normalizePath (path, parent) { - path = path.replace(/\/$/, ''); +function normalizePath (path, parent, strict) { + if (!strict) { path = path.replace(/\/$/, ''); } if (path[0] === '/') { return path } if (parent == null) { return path } return cleanPath(((parent.path) + "/" + path)) @@ -1483,6 +1519,8 @@ function resolveRecordPath (path, record) { var positionStore = Object.create(null); function setupScroll () { + // Fix for #1585 for Firefox + window.history.replaceState({ key: getStateKey() }, ''); window.addEventListener('popstate', function (e) { saveScrollPosition(); if (e.state && e.state.key) { @@ -1514,25 +1552,21 @@ function handleScroll ( router.app.$nextTick(function () { var position = getScrollPosition(); var shouldScroll = behavior(to, from, isPop ? position : null); + if (!shouldScroll) { return } - var isObject = typeof shouldScroll === 'object'; - if (isObject && typeof shouldScroll.selector === 'string') { - var el = document.querySelector(shouldScroll.selector); - if (el) { - var offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}; - offset = normalizeOffset(offset); - position = getElementPosition(el, offset); - } else if (isValidPosition(shouldScroll)) { - position = normalizePosition(shouldScroll); - } - } else if (isObject && isValidPosition(shouldScroll)) { - position = normalizePosition(shouldScroll); - } - if (position) { - window.scrollTo(position.x, position.y); + if (typeof shouldScroll.then === 'function') { + shouldScroll.then(function (shouldScroll) { + scrollToPosition((shouldScroll), position); + }).catch(function (err) { + if (process.env.NODE_ENV !== 'production') { + assert(false, err.toString()); + } + }); + } else { + scrollToPosition(shouldScroll, position); } }); } @@ -1586,6 +1620,26 @@ function isNumber (v) { return typeof v === 'number' } +function scrollToPosition (shouldScroll, position) { + var isObject = typeof shouldScroll === 'object'; + if (isObject && typeof shouldScroll.selector === 'string') { + var el = document.querySelector(shouldScroll.selector); + if (el) { + var offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}; + offset = normalizeOffset(offset); + position = getElementPosition(el, offset); + } else if (isValidPosition(shouldScroll)) { + position = normalizePosition(shouldScroll); + } + } else if (isObject && isValidPosition(shouldScroll)) { + position = normalizePosition(shouldScroll); + } + + if (position) { + window.scrollTo(position.x, position.y); + } +} + /* */ var supportsPushState = inBrowser && (function () { @@ -1681,7 +1735,7 @@ function resolveAsyncComponents (matched) { pending++; var resolve = once(function (resolvedDef) { - if (resolvedDef.__esModule && resolvedDef.default) { + if (isESModule(resolvedDef)) { resolvedDef = resolvedDef.default; } // save resolved on async factory in case it's used elsewhere @@ -1747,6 +1801,14 @@ function flatten (arr) { return Array.prototype.concat.apply([], arr) } +var hasSymbol = + typeof Symbol === 'function' && + typeof Symbol.toStringTag === 'symbol'; + +function isESModule (obj) { + return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module') +} + // in Webpack 2, require.ensure now also returns a Promise // so the resolve/reject functions may get called an extra time // if the user uses an arrow function shorthand that happens to @@ -2075,9 +2137,18 @@ var HTML5History = (function (History$$1) { setupScroll(); } + var initLocation = getLocation(this.base); window.addEventListener('popstate', function (e) { var current = this$1.current; - this$1.transitionTo(getLocation(this$1.base), function (route) { + + // Avoiding first `popstate` event dispatched in some browsers but first + // history route not updated since async guard at the same time. + var location = getLocation(this$1.base); + if (this$1.current === START && location === initLocation) { + return + } + + this$1.transitionTo(location, function (route) { if (expectScroll) { handleScroll(router, route, current, true); } @@ -2161,26 +2232,50 @@ var HashHistory = (function (History$$1) { HashHistory.prototype.setupListeners = function setupListeners () { var this$1 = this; - window.addEventListener('hashchange', function () { + var router = this.router; + var expectScroll = router.options.scrollBehavior; + var supportsScroll = supportsPushState && expectScroll; + + if (supportsScroll) { + setupScroll(); + } + + window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', function () { + var current = this$1.current; if (!ensureSlash()) { return } this$1.transitionTo(getHash(), function (route) { - replaceHash(route.fullPath); + if (supportsScroll) { + handleScroll(this$1.router, route, current, true); + } + if (!supportsPushState) { + replaceHash(route.fullPath); + } }); }); }; HashHistory.prototype.push = function push (location, onComplete, onAbort) { + var this$1 = this; + + var ref = this; + var fromRoute = ref.current; this.transitionTo(location, function (route) { pushHash(route.fullPath); + handleScroll(this$1.router, route, fromRoute, false); onComplete && onComplete(route); }, onAbort); }; HashHistory.prototype.replace = function replace (location, onComplete, onAbort) { + var this$1 = this; + + var ref = this; + var fromRoute = ref.current; this.transitionTo(location, function (route) { replaceHash(route.fullPath); + handleScroll(this$1.router, route, fromRoute, false); onComplete && onComplete(route); }, onAbort); }; @@ -2230,15 +2325,27 @@ function getHash () { return index === -1 ? '' : href.slice(index + 1) } +function getUrl (path) { + var href = window.location.href; + var i = href.indexOf('#'); + var base = i >= 0 ? href.slice(0, i) : href; + return (base + "#" + path) +} + function pushHash (path) { - window.location.hash = path; + if (supportsPushState) { + pushState(getUrl(path)); + } else { + window.location.hash = path; + } } function replaceHash (path) { - var href = window.location.href; - var i = href.indexOf('#'); - var base = i >= 0 ? href.slice(0, i) : href; - window.location.replace((base + "#" + path)); + if (supportsPushState) { + replaceState(getUrl(path)); + } else { + window.location.replace(getUrl(path)); + } } /* */ @@ -2340,7 +2447,7 @@ var VueRouter = function VueRouter (options) { } }; -var prototypeAccessors = { currentRoute: {} }; +var prototypeAccessors = { currentRoute: { configurable: true } }; VueRouter.prototype.match = function match ( raw, @@ -2498,7 +2605,7 @@ function createHref (base, fullPath, mode) { } VueRouter.install = install; -VueRouter.version = '2.7.0'; +VueRouter.version = '2.8.0'; if (inBrowser && window.Vue) { window.Vue.use(VueRouter); diff --git a/dist/vue-router.esm.js b/dist/vue-router.esm.js index a2dc19c1f..2409a8405 100644 --- a/dist/vue-router.esm.js +++ b/dist/vue-router.esm.js @@ -1,5 +1,5 @@ /** - * vue-router v2.7.0 + * vue-router v2.8.0 * (c) 2017 Evan You * @license MIT */ @@ -87,7 +87,7 @@ var View = { } } - // also regiseter instance in prepatch hook + // also register instance in prepatch hook // in case the same component instance is reused across different routes ;(data.hook || (data.hook = {})).prepatch = function (_, vnode) { matched.instances[name] = vnode.componentInstance; @@ -95,6 +95,14 @@ var View = { // resolve props data.props = resolveProps(route, matched.props && matched.props[name]); + data.attrs = {}; + + for (var key in data.props) { + if (!('props' in component) || !(key in component.props)) { + data.attrs[key] = data.props[key]; + delete data.props[key]; + } + } return h(component, data, children) } @@ -152,8 +160,7 @@ function resolveQuery ( parsedQuery = {}; } for (var key in extraQuery) { - var val = extraQuery[key]; - parsedQuery[key] = Array.isArray(val) ? val.slice() : val; + parsedQuery[key] = extraQuery[key]; } return parsedQuery } @@ -230,12 +237,18 @@ function createRoute ( router ) { var stringifyQuery$$1 = router && router.options.stringifyQuery; + + var query = location.query || {}; + try { + query = clone(query); + } catch (e) {} + var route = { name: location.name || (record && record.name), meta: (record && record.meta) || {}, path: location.path || '/', hash: location.hash || '', - query: location.query || {}, + query: query, params: location.params || {}, fullPath: getFullPath(location, stringifyQuery$$1), matched: record ? formatMatch(record) : [] @@ -246,6 +259,20 @@ function createRoute ( return Object.freeze(route) } +function clone (value) { + if (Array.isArray(value)) { + return value.map(clone) + } else if (value && typeof value === 'object') { + var res = {}; + for (var key in value) { + res[key] = clone(value[key]); + } + return res + } else { + return value + } +} + // the starting route that represents the initial state var START = createRoute(null, { path: '/' @@ -299,6 +326,8 @@ function isObjectEqual (a, b) { if ( a === void 0 ) a = {}; if ( b === void 0 ) b = {}; + // handle null value #1566 + if (!a || !b) { return a === b } var aKeys = Object.keys(a); var bKeys = Object.keys(b); if (aKeys.length !== bKeys.length) { @@ -478,7 +507,7 @@ function findAnchor (children) { var _Vue; function install (Vue) { - if (install.installed) { return } + if (install.installed && _Vue === Vue) { return } install.installed = true; _Vue = Vue; @@ -600,14 +629,14 @@ function cleanPath (path) { return path.replace(/\/\//g, '/') } -var index$1 = Array.isArray || function (arr) { +var isarray = Array.isArray || function (arr) { return Object.prototype.toString.call(arr) == '[object Array]'; }; /** * Expose `pathToRegexp`. */ -var index = pathToRegexp; +var pathToRegexp_1 = pathToRegexp; var parse_1 = parse; var compile_1 = compile; var tokensToFunction_1 = tokensToFunction; @@ -784,7 +813,7 @@ function tokensToFunction (tokens) { } } - if (index$1(value)) { + if (isarray(value)) { if (!token.repeat) { throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`') } @@ -935,7 +964,7 @@ function stringToRegexp (path, keys, options) { * @return {!RegExp} */ function tokensToRegExp (tokens, keys, options) { - if (!index$1(keys)) { + if (!isarray(keys)) { options = /** @type {!Object} */ (keys || options); keys = []; } @@ -1011,7 +1040,7 @@ function tokensToRegExp (tokens, keys, options) { * @return {!RegExp} */ function pathToRegexp (path, keys, options) { - if (!index$1(keys)) { + if (!isarray(keys)) { options = /** @type {!Object} */ (keys || options); keys = []; } @@ -1022,20 +1051,21 @@ function pathToRegexp (path, keys, options) { return regexpToRegexp(path, /** @type {!Array} */ (keys)) } - if (index$1(path)) { + if (isarray(path)) { return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options) } return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options) } -index.parse = parse_1; -index.compile = compile_1; -index.tokensToFunction = tokensToFunction_1; -index.tokensToRegExp = tokensToRegExp_1; +pathToRegexp_1.parse = parse_1; +pathToRegexp_1.compile = compile_1; +pathToRegexp_1.tokensToFunction = tokensToFunction_1; +pathToRegexp_1.tokensToRegExp = tokensToRegExp_1; /* */ +// $flow-disable-line var regexpCompileCache = Object.create(null); function fillParams ( @@ -1046,7 +1076,7 @@ function fillParams ( try { var filler = regexpCompileCache[path] || - (regexpCompileCache[path] = index.compile(path)); + (regexpCompileCache[path] = pathToRegexp_1.compile(path)); return filler(params || {}, { pretty: true }) } catch (e) { if (process.env.NODE_ENV !== 'production') { @@ -1066,7 +1096,9 @@ function createRouteMap ( ) { // the path list is used to control path matching priority var pathList = oldPathList || []; + // $flow-disable-line var pathMap = oldPathMap || Object.create(null); + // $flow-disable-line var nameMap = oldNameMap || Object.create(null); routes.forEach(function (route) { @@ -1108,8 +1140,12 @@ function addRouteRecord ( ); } - var normalizedPath = normalizePath(path, parent); var pathToRegexpOptions = route.pathToRegexpOptions || {}; + var normalizedPath = normalizePath( + path, + parent, + pathToRegexpOptions.strict + ); if (typeof route.caseSensitive === 'boolean') { pathToRegexpOptions.sensitive = route.caseSensitive; @@ -1197,9 +1233,9 @@ function addRouteRecord ( } function compileRouteRegex (path, pathToRegexpOptions) { - var regex = index(path, [], pathToRegexpOptions); + var regex = pathToRegexp_1(path, [], pathToRegexpOptions); if (process.env.NODE_ENV !== 'production') { - var keys = {}; + var keys = Object.create(null); regex.keys.forEach(function (key) { warn(!keys[key.name], ("Duplicate param keys in route with path: \"" + path + "\"")); keys[key.name] = true; @@ -1208,8 +1244,8 @@ function compileRouteRegex (path, pathToRegexpOptions) { return regex } -function normalizePath (path, parent) { - path = path.replace(/\/$/, ''); +function normalizePath (path, parent, strict) { + if (!strict) { path = path.replace(/\/$/, ''); } if (path[0] === '/') { return path } if (parent == null) { return path } return cleanPath(((parent.path) + "/" + path)) @@ -1481,6 +1517,8 @@ function resolveRecordPath (path, record) { var positionStore = Object.create(null); function setupScroll () { + // Fix for #1585 for Firefox + window.history.replaceState({ key: getStateKey() }, ''); window.addEventListener('popstate', function (e) { saveScrollPosition(); if (e.state && e.state.key) { @@ -1512,25 +1550,21 @@ function handleScroll ( router.app.$nextTick(function () { var position = getScrollPosition(); var shouldScroll = behavior(to, from, isPop ? position : null); + if (!shouldScroll) { return } - var isObject = typeof shouldScroll === 'object'; - if (isObject && typeof shouldScroll.selector === 'string') { - var el = document.querySelector(shouldScroll.selector); - if (el) { - var offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}; - offset = normalizeOffset(offset); - position = getElementPosition(el, offset); - } else if (isValidPosition(shouldScroll)) { - position = normalizePosition(shouldScroll); - } - } else if (isObject && isValidPosition(shouldScroll)) { - position = normalizePosition(shouldScroll); - } - if (position) { - window.scrollTo(position.x, position.y); + if (typeof shouldScroll.then === 'function') { + shouldScroll.then(function (shouldScroll) { + scrollToPosition((shouldScroll), position); + }).catch(function (err) { + if (process.env.NODE_ENV !== 'production') { + assert(false, err.toString()); + } + }); + } else { + scrollToPosition(shouldScroll, position); } }); } @@ -1584,6 +1618,26 @@ function isNumber (v) { return typeof v === 'number' } +function scrollToPosition (shouldScroll, position) { + var isObject = typeof shouldScroll === 'object'; + if (isObject && typeof shouldScroll.selector === 'string') { + var el = document.querySelector(shouldScroll.selector); + if (el) { + var offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}; + offset = normalizeOffset(offset); + position = getElementPosition(el, offset); + } else if (isValidPosition(shouldScroll)) { + position = normalizePosition(shouldScroll); + } + } else if (isObject && isValidPosition(shouldScroll)) { + position = normalizePosition(shouldScroll); + } + + if (position) { + window.scrollTo(position.x, position.y); + } +} + /* */ var supportsPushState = inBrowser && (function () { @@ -1679,7 +1733,7 @@ function resolveAsyncComponents (matched) { pending++; var resolve = once(function (resolvedDef) { - if (resolvedDef.__esModule && resolvedDef.default) { + if (isESModule(resolvedDef)) { resolvedDef = resolvedDef.default; } // save resolved on async factory in case it's used elsewhere @@ -1745,6 +1799,14 @@ function flatten (arr) { return Array.prototype.concat.apply([], arr) } +var hasSymbol = + typeof Symbol === 'function' && + typeof Symbol.toStringTag === 'symbol'; + +function isESModule (obj) { + return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module') +} + // in Webpack 2, require.ensure now also returns a Promise // so the resolve/reject functions may get called an extra time // if the user uses an arrow function shorthand that happens to @@ -2073,9 +2135,18 @@ var HTML5History = (function (History$$1) { setupScroll(); } + var initLocation = getLocation(this.base); window.addEventListener('popstate', function (e) { var current = this$1.current; - this$1.transitionTo(getLocation(this$1.base), function (route) { + + // Avoiding first `popstate` event dispatched in some browsers but first + // history route not updated since async guard at the same time. + var location = getLocation(this$1.base); + if (this$1.current === START && location === initLocation) { + return + } + + this$1.transitionTo(location, function (route) { if (expectScroll) { handleScroll(router, route, current, true); } @@ -2159,26 +2230,50 @@ var HashHistory = (function (History$$1) { HashHistory.prototype.setupListeners = function setupListeners () { var this$1 = this; - window.addEventListener('hashchange', function () { + var router = this.router; + var expectScroll = router.options.scrollBehavior; + var supportsScroll = supportsPushState && expectScroll; + + if (supportsScroll) { + setupScroll(); + } + + window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', function () { + var current = this$1.current; if (!ensureSlash()) { return } this$1.transitionTo(getHash(), function (route) { - replaceHash(route.fullPath); + if (supportsScroll) { + handleScroll(this$1.router, route, current, true); + } + if (!supportsPushState) { + replaceHash(route.fullPath); + } }); }); }; HashHistory.prototype.push = function push (location, onComplete, onAbort) { + var this$1 = this; + + var ref = this; + var fromRoute = ref.current; this.transitionTo(location, function (route) { pushHash(route.fullPath); + handleScroll(this$1.router, route, fromRoute, false); onComplete && onComplete(route); }, onAbort); }; HashHistory.prototype.replace = function replace (location, onComplete, onAbort) { + var this$1 = this; + + var ref = this; + var fromRoute = ref.current; this.transitionTo(location, function (route) { replaceHash(route.fullPath); + handleScroll(this$1.router, route, fromRoute, false); onComplete && onComplete(route); }, onAbort); }; @@ -2228,15 +2323,27 @@ function getHash () { return index === -1 ? '' : href.slice(index + 1) } +function getUrl (path) { + var href = window.location.href; + var i = href.indexOf('#'); + var base = i >= 0 ? href.slice(0, i) : href; + return (base + "#" + path) +} + function pushHash (path) { - window.location.hash = path; + if (supportsPushState) { + pushState(getUrl(path)); + } else { + window.location.hash = path; + } } function replaceHash (path) { - var href = window.location.href; - var i = href.indexOf('#'); - var base = i >= 0 ? href.slice(0, i) : href; - window.location.replace((base + "#" + path)); + if (supportsPushState) { + replaceState(getUrl(path)); + } else { + window.location.replace(getUrl(path)); + } } /* */ @@ -2338,7 +2445,7 @@ var VueRouter = function VueRouter (options) { } }; -var prototypeAccessors = { currentRoute: {} }; +var prototypeAccessors = { currentRoute: { configurable: true } }; VueRouter.prototype.match = function match ( raw, @@ -2496,7 +2603,7 @@ function createHref (base, fullPath, mode) { } VueRouter.install = install; -VueRouter.version = '2.7.0'; +VueRouter.version = '2.8.0'; if (inBrowser && window.Vue) { window.Vue.use(VueRouter); diff --git a/dist/vue-router.js b/dist/vue-router.js index c7c26bb64..905b8c79a 100644 --- a/dist/vue-router.js +++ b/dist/vue-router.js @@ -1,5 +1,5 @@ /** - * vue-router v2.7.0 + * vue-router v2.8.0 * (c) 2017 Evan You * @license MIT */ @@ -93,7 +93,7 @@ var View = { } } - // also regiseter instance in prepatch hook + // also register instance in prepatch hook // in case the same component instance is reused across different routes ;(data.hook || (data.hook = {})).prepatch = function (_, vnode) { matched.instances[name] = vnode.componentInstance; @@ -101,6 +101,14 @@ var View = { // resolve props data.props = resolveProps(route, matched.props && matched.props[name]); + data.attrs = {}; + + for (var key in data.props) { + if (!('props' in component) || !(key in component.props)) { + data.attrs[key] = data.props[key]; + delete data.props[key]; + } + } return h(component, data, children) } @@ -158,8 +166,7 @@ function resolveQuery ( parsedQuery = {}; } for (var key in extraQuery) { - var val = extraQuery[key]; - parsedQuery[key] = Array.isArray(val) ? val.slice() : val; + parsedQuery[key] = extraQuery[key]; } return parsedQuery } @@ -236,12 +243,18 @@ function createRoute ( router ) { var stringifyQuery$$1 = router && router.options.stringifyQuery; + + var query = location.query || {}; + try { + query = clone(query); + } catch (e) {} + var route = { name: location.name || (record && record.name), meta: (record && record.meta) || {}, path: location.path || '/', hash: location.hash || '', - query: location.query || {}, + query: query, params: location.params || {}, fullPath: getFullPath(location, stringifyQuery$$1), matched: record ? formatMatch(record) : [] @@ -252,6 +265,20 @@ function createRoute ( return Object.freeze(route) } +function clone (value) { + if (Array.isArray(value)) { + return value.map(clone) + } else if (value && typeof value === 'object') { + var res = {}; + for (var key in value) { + res[key] = clone(value[key]); + } + return res + } else { + return value + } +} + // the starting route that represents the initial state var START = createRoute(null, { path: '/' @@ -305,6 +332,8 @@ function isObjectEqual (a, b) { if ( a === void 0 ) a = {}; if ( b === void 0 ) b = {}; + // handle null value #1566 + if (!a || !b) { return a === b } var aKeys = Object.keys(a); var bKeys = Object.keys(b); if (aKeys.length !== bKeys.length) { @@ -484,7 +513,7 @@ function findAnchor (children) { var _Vue; function install (Vue) { - if (install.installed) { return } + if (install.installed && _Vue === Vue) { return } install.installed = true; _Vue = Vue; @@ -606,14 +635,14 @@ function cleanPath (path) { return path.replace(/\/\//g, '/') } -var index$1 = Array.isArray || function (arr) { +var isarray = Array.isArray || function (arr) { return Object.prototype.toString.call(arr) == '[object Array]'; }; /** * Expose `pathToRegexp`. */ -var index = pathToRegexp; +var pathToRegexp_1 = pathToRegexp; var parse_1 = parse; var compile_1 = compile; var tokensToFunction_1 = tokensToFunction; @@ -790,7 +819,7 @@ function tokensToFunction (tokens) { } } - if (index$1(value)) { + if (isarray(value)) { if (!token.repeat) { throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`') } @@ -941,7 +970,7 @@ function stringToRegexp (path, keys, options) { * @return {!RegExp} */ function tokensToRegExp (tokens, keys, options) { - if (!index$1(keys)) { + if (!isarray(keys)) { options = /** @type {!Object} */ (keys || options); keys = []; } @@ -1017,7 +1046,7 @@ function tokensToRegExp (tokens, keys, options) { * @return {!RegExp} */ function pathToRegexp (path, keys, options) { - if (!index$1(keys)) { + if (!isarray(keys)) { options = /** @type {!Object} */ (keys || options); keys = []; } @@ -1028,20 +1057,21 @@ function pathToRegexp (path, keys, options) { return regexpToRegexp(path, /** @type {!Array} */ (keys)) } - if (index$1(path)) { + if (isarray(path)) { return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options) } return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options) } -index.parse = parse_1; -index.compile = compile_1; -index.tokensToFunction = tokensToFunction_1; -index.tokensToRegExp = tokensToRegExp_1; +pathToRegexp_1.parse = parse_1; +pathToRegexp_1.compile = compile_1; +pathToRegexp_1.tokensToFunction = tokensToFunction_1; +pathToRegexp_1.tokensToRegExp = tokensToRegExp_1; /* */ +// $flow-disable-line var regexpCompileCache = Object.create(null); function fillParams ( @@ -1052,7 +1082,7 @@ function fillParams ( try { var filler = regexpCompileCache[path] || - (regexpCompileCache[path] = index.compile(path)); + (regexpCompileCache[path] = pathToRegexp_1.compile(path)); return filler(params || {}, { pretty: true }) } catch (e) { { @@ -1072,7 +1102,9 @@ function createRouteMap ( ) { // the path list is used to control path matching priority var pathList = oldPathList || []; + // $flow-disable-line var pathMap = oldPathMap || Object.create(null); + // $flow-disable-line var nameMap = oldNameMap || Object.create(null); routes.forEach(function (route) { @@ -1114,8 +1146,12 @@ function addRouteRecord ( ); } - var normalizedPath = normalizePath(path, parent); var pathToRegexpOptions = route.pathToRegexpOptions || {}; + var normalizedPath = normalizePath( + path, + parent, + pathToRegexpOptions.strict + ); if (typeof route.caseSensitive === 'boolean') { pathToRegexpOptions.sensitive = route.caseSensitive; @@ -1203,9 +1239,9 @@ function addRouteRecord ( } function compileRouteRegex (path, pathToRegexpOptions) { - var regex = index(path, [], pathToRegexpOptions); + var regex = pathToRegexp_1(path, [], pathToRegexpOptions); { - var keys = {}; + var keys = Object.create(null); regex.keys.forEach(function (key) { warn(!keys[key.name], ("Duplicate param keys in route with path: \"" + path + "\"")); keys[key.name] = true; @@ -1214,8 +1250,8 @@ function compileRouteRegex (path, pathToRegexpOptions) { return regex } -function normalizePath (path, parent) { - path = path.replace(/\/$/, ''); +function normalizePath (path, parent, strict) { + if (!strict) { path = path.replace(/\/$/, ''); } if (path[0] === '/') { return path } if (parent == null) { return path } return cleanPath(((parent.path) + "/" + path)) @@ -1487,6 +1523,8 @@ function resolveRecordPath (path, record) { var positionStore = Object.create(null); function setupScroll () { + // Fix for #1585 for Firefox + window.history.replaceState({ key: getStateKey() }, ''); window.addEventListener('popstate', function (e) { saveScrollPosition(); if (e.state && e.state.key) { @@ -1518,25 +1556,21 @@ function handleScroll ( router.app.$nextTick(function () { var position = getScrollPosition(); var shouldScroll = behavior(to, from, isPop ? position : null); + if (!shouldScroll) { return } - var isObject = typeof shouldScroll === 'object'; - if (isObject && typeof shouldScroll.selector === 'string') { - var el = document.querySelector(shouldScroll.selector); - if (el) { - var offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}; - offset = normalizeOffset(offset); - position = getElementPosition(el, offset); - } else if (isValidPosition(shouldScroll)) { - position = normalizePosition(shouldScroll); - } - } else if (isObject && isValidPosition(shouldScroll)) { - position = normalizePosition(shouldScroll); - } - if (position) { - window.scrollTo(position.x, position.y); + if (typeof shouldScroll.then === 'function') { + shouldScroll.then(function (shouldScroll) { + scrollToPosition((shouldScroll), position); + }).catch(function (err) { + { + assert(false, err.toString()); + } + }); + } else { + scrollToPosition(shouldScroll, position); } }); } @@ -1590,6 +1624,26 @@ function isNumber (v) { return typeof v === 'number' } +function scrollToPosition (shouldScroll, position) { + var isObject = typeof shouldScroll === 'object'; + if (isObject && typeof shouldScroll.selector === 'string') { + var el = document.querySelector(shouldScroll.selector); + if (el) { + var offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}; + offset = normalizeOffset(offset); + position = getElementPosition(el, offset); + } else if (isValidPosition(shouldScroll)) { + position = normalizePosition(shouldScroll); + } + } else if (isObject && isValidPosition(shouldScroll)) { + position = normalizePosition(shouldScroll); + } + + if (position) { + window.scrollTo(position.x, position.y); + } +} + /* */ var supportsPushState = inBrowser && (function () { @@ -1685,7 +1739,7 @@ function resolveAsyncComponents (matched) { pending++; var resolve = once(function (resolvedDef) { - if (resolvedDef.__esModule && resolvedDef.default) { + if (isESModule(resolvedDef)) { resolvedDef = resolvedDef.default; } // save resolved on async factory in case it's used elsewhere @@ -1751,6 +1805,14 @@ function flatten (arr) { return Array.prototype.concat.apply([], arr) } +var hasSymbol = + typeof Symbol === 'function' && + typeof Symbol.toStringTag === 'symbol'; + +function isESModule (obj) { + return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module') +} + // in Webpack 2, require.ensure now also returns a Promise // so the resolve/reject functions may get called an extra time // if the user uses an arrow function shorthand that happens to @@ -2079,9 +2141,18 @@ var HTML5History = (function (History$$1) { setupScroll(); } + var initLocation = getLocation(this.base); window.addEventListener('popstate', function (e) { var current = this$1.current; - this$1.transitionTo(getLocation(this$1.base), function (route) { + + // Avoiding first `popstate` event dispatched in some browsers but first + // history route not updated since async guard at the same time. + var location = getLocation(this$1.base); + if (this$1.current === START && location === initLocation) { + return + } + + this$1.transitionTo(location, function (route) { if (expectScroll) { handleScroll(router, route, current, true); } @@ -2165,26 +2236,50 @@ var HashHistory = (function (History$$1) { HashHistory.prototype.setupListeners = function setupListeners () { var this$1 = this; - window.addEventListener('hashchange', function () { + var router = this.router; + var expectScroll = router.options.scrollBehavior; + var supportsScroll = supportsPushState && expectScroll; + + if (supportsScroll) { + setupScroll(); + } + + window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', function () { + var current = this$1.current; if (!ensureSlash()) { return } this$1.transitionTo(getHash(), function (route) { - replaceHash(route.fullPath); + if (supportsScroll) { + handleScroll(this$1.router, route, current, true); + } + if (!supportsPushState) { + replaceHash(route.fullPath); + } }); }); }; HashHistory.prototype.push = function push (location, onComplete, onAbort) { + var this$1 = this; + + var ref = this; + var fromRoute = ref.current; this.transitionTo(location, function (route) { pushHash(route.fullPath); + handleScroll(this$1.router, route, fromRoute, false); onComplete && onComplete(route); }, onAbort); }; HashHistory.prototype.replace = function replace (location, onComplete, onAbort) { + var this$1 = this; + + var ref = this; + var fromRoute = ref.current; this.transitionTo(location, function (route) { replaceHash(route.fullPath); + handleScroll(this$1.router, route, fromRoute, false); onComplete && onComplete(route); }, onAbort); }; @@ -2234,15 +2329,27 @@ function getHash () { return index === -1 ? '' : href.slice(index + 1) } +function getUrl (path) { + var href = window.location.href; + var i = href.indexOf('#'); + var base = i >= 0 ? href.slice(0, i) : href; + return (base + "#" + path) +} + function pushHash (path) { - window.location.hash = path; + if (supportsPushState) { + pushState(getUrl(path)); + } else { + window.location.hash = path; + } } function replaceHash (path) { - var href = window.location.href; - var i = href.indexOf('#'); - var base = i >= 0 ? href.slice(0, i) : href; - window.location.replace((base + "#" + path)); + if (supportsPushState) { + replaceState(getUrl(path)); + } else { + window.location.replace(getUrl(path)); + } } /* */ @@ -2344,7 +2451,7 @@ var VueRouter = function VueRouter (options) { } }; -var prototypeAccessors = { currentRoute: {} }; +var prototypeAccessors = { currentRoute: { configurable: true } }; VueRouter.prototype.match = function match ( raw, @@ -2502,7 +2609,7 @@ function createHref (base, fullPath, mode) { } VueRouter.install = install; -VueRouter.version = '2.7.0'; +VueRouter.version = '2.8.0'; if (inBrowser && window.Vue) { window.Vue.use(VueRouter); diff --git a/dist/vue-router.min.js b/dist/vue-router.min.js index 4f71c8057..1d343cf02 100644 --- a/dist/vue-router.min.js +++ b/dist/vue-router.min.js @@ -1,6 +1,6 @@ /** - * vue-router v2.7.0 + * vue-router v2.8.0 * (c) 2017 Evan You * @license MIT */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueRouter=e()}(this,function(){"use strict";function t(t,e){}function e(t){return Object.prototype.toString.call(t).indexOf("Error")>-1}function r(t,e){switch(typeof e){case"undefined":return;case"object":return e;case"function":return e(t);case"boolean":return e?t.params:void 0}}function n(t,e,r){void 0===e&&(e={});var n,i=r||o;try{n=i(t||"")}catch(t){n={}}for(var a in e){var u=e[a];n[a]=Array.isArray(u)?u.slice():u}return n}function o(t){var e={};return(t=t.trim().replace(/^(\?|#|&)/,""))?(t.split("&").forEach(function(t){var r=t.replace(/\+/g," ").split("="),n=$t(r.shift()),o=r.length>0?$t(r.join("=")):null;void 0===e[n]?e[n]=o:Array.isArray(e[n])?e[n].push(o):e[n]=[e[n],o]}),e):e}function i(t){var e=t?Object.keys(t).map(function(e){var r=t[e];if(void 0===r)return"";if(null===r)return Tt(e);if(Array.isArray(r)){var n=[];return r.forEach(function(t){void 0!==t&&(null===t?n.push(Tt(e)):n.push(Tt(e)+"="+Tt(t)))}),n.join("&")}return Tt(e)+"="+Tt(r)}).filter(function(t){return t.length>0}).join("&"):null;return e?"?"+e:""}function a(t,e,r,n){var o=n&&n.options.stringifyQuery,i={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||"/",hash:e.hash||"",query:e.query||{},params:e.params||{},fullPath:c(e,o),matched:t?u(t):[]};return r&&(i.redirectedFrom=c(r,o)),Object.freeze(i)}function u(t){for(var e=[];t;)e.unshift(t),t=t.parent;return e}function c(t,e){var r=t.path,n=t.query;void 0===n&&(n={});var o=t.hash;void 0===o&&(o="");var a=e||i;return(r||"/")+a(n)+o}function s(t,e){return e===qt?t===e:!!e&&(t.path&&e.path?t.path.replace(St,"")===e.path.replace(St,"")&&t.hash===e.hash&&p(t.query,e.query):!(!t.name||!e.name)&&(t.name===e.name&&t.hash===e.hash&&p(t.query,e.query)&&p(t.params,e.params)))}function p(t,e){void 0===t&&(t={}),void 0===e&&(e={});var r=Object.keys(t),n=Object.keys(e);return r.length===n.length&&r.every(function(r){var n=t[r],o=e[r];return"object"==typeof n&&"object"==typeof o?p(n,o):String(n)===String(o)})}function f(t,e){return 0===t.path.replace(St,"/").indexOf(e.path.replace(St,"/"))&&(!e.hash||t.hash===e.hash)&&h(t.query,e.query)}function h(t,e){for(var r in e)if(!(r in t))return!1;return!0}function l(t){if(!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey||t.defaultPrevented||void 0!==t.button&&0!==t.button)){if(t.currentTarget&&t.currentTarget.getAttribute){var e=t.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(e))return}return t.preventDefault&&t.preventDefault(),!0}}function d(t){if(t)for(var e,r=0;r=0&&(e=t.slice(n),t=t.slice(0,n));var o=t.indexOf("?");return o>=0&&(r=t.slice(o+1),t=t.slice(0,o)),{path:t,query:r,hash:e}}function g(t){return t.replace(/\/\//g,"/")}function b(t,e){for(var r,n=[],o=0,i=0,a="",u=e&&e.delimiter||"/";null!=(r=Ft.exec(t));){var c=r[0],s=r[1],p=r.index;if(a+=t.slice(i,p),i=p+c.length,s)a+=s[1];else{var f=t[i],h=r[2],l=r[3],d=r[4],y=r[5],v=r[6],m=r[7];a&&(n.push(a),a="");var g=null!=h&&null!=f&&f!==h,b="+"===v||"*"===v,w="?"===v||"*"===v,x=r[2]||u,k=d||y;n.push({name:l||o++,prefix:h||"",delimiter:x,optional:w,repeat:b,partial:g,asterisk:!!m,pattern:k?E(k):m?".*":"[^"+R(x)+"]+?"})}}return i-1&&(o.params[h]=r.params[h]);if(u)return o.path=S(u.path,o.params,'named route "'+a+'"'),i(u,o,n)}else if(o.path){o.params={};for(var l=0;l=t.length?r():t[o]?e(t[o],function(){n(o+1)}):n(o+1)};n(0)}function nt(t){return function(r,n,o){var i=!1,a=0,u=null;ot(t,function(t,r,n,c){if("function"==typeof t&&void 0===t.cid){i=!0,a++;var s,p=at(function(e){e.__esModule&&e.default&&(e=e.default),t.resolved="function"==typeof e?e:Ot.extend(e),n.components[c]=e,--a<=0&&o()}),f=at(function(t){var r="Failed to resolve async component "+c+": "+t;u||(u=e(t)?t:new Error(r),o(u))});try{s=t(p,f)}catch(t){f(t)}if(s)if("function"==typeof s.then)s.then(p,f);else{var h=s.component;h&&"function"==typeof h.then&&h.then(p,f)}}}),i||o()}}function ot(t,e){return it(t.map(function(t){return Object.keys(t.components).map(function(r){return e(t.components[r],t.instances[r],t,r)})}))}function it(t){return Array.prototype.concat.apply([],t)}function at(t){var e=!1;return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(!e)return e=!0,t.apply(this,r)}}function ut(t){if(!t)if(Ht){var e=document.querySelector("base");t=(t=e&&e.getAttribute("href")||"/").replace(/^https?:\/\/[^\/]+/,"")}else t="/";return"/"!==t.charAt(0)&&(t="/"+t),t.replace(/\/$/,"")}function ct(t,e){var r,n=Math.max(t.length,e.length);for(r=0;r=0?e.slice(0,r):e;window.location.replace(n+"#"+t)}function Rt(t,e){return t.push(e),function(){var r=t.indexOf(e);r>-1&&t.splice(r,1)}}function Et(t,e,r){var n="hash"===r?"#"+e:e;return t?g(t+"/"+n):n}var Ot,Ct={name:"router-view",functional:!0,props:{name:{type:String,default:"default"}},render:function(t,e){var n=e.props,o=e.children,i=e.parent,a=e.data;a.routerView=!0;for(var u=i.$createElement,c=n.name,s=i.$route,p=i._routerViewCache||(i._routerViewCache={}),f=0,h=!1;i&&i._routerRoot!==i;)i.$vnode&&i.$vnode.data.routerView&&f++,i._inactive&&(h=!0),i=i.$parent;if(a.routerViewDepth=f,h)return u(p[c],a,o);var l=s.matched[f];if(!l)return p[c]=null,u();var d=p[c]=l.components[c];return a.registerRouteInstance=function(t,e){var r=l.instances[c];(e&&r!==t||!e&&r===t)&&(l.instances[c]=e)},(a.hook||(a.hook={})).prepatch=function(t,e){l.instances[c]=e.componentInstance},a.props=r(s,l.props&&l.props[c]),u(d,a,o)}},At=/[!'()*]/g,jt=function(t){return"%"+t.charCodeAt(0).toString(16)},_t=/%2C/g,Tt=function(t){return encodeURIComponent(t).replace(At,jt).replace(_t,",")},$t=decodeURIComponent,St=/\/?$/,qt=a(null,{path:"/"}),Lt=[String,Object],Pt=[String,Array],Ut={name:"router-link",props:{to:{type:Lt,required:!0},tag:{type:String,default:"a"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,event:{type:Pt,default:"click"}},render:function(t){var e=this,r=this.$router,n=this.$route,o=r.resolve(this.to,n,this.append),i=o.location,u=o.route,c=o.href,p={},h=r.options.linkActiveClass,y=r.options.linkExactActiveClass,v=null==h?"router-link-active":h,m=null==y?"router-link-exact-active":y,g=null==this.activeClass?v:this.activeClass,b=null==this.exactActiveClass?m:this.exactActiveClass,w=i.path?a(null,i,null,r):u;p[b]=s(n,w),p[g]=this.exact?p[b]:f(n,w);var x=function(t){l(t)&&(e.replace?r.replace(i):r.push(i))},k={click:l};Array.isArray(this.event)?this.event.forEach(function(t){k[t]=x}):k[this.event]=x;var R={class:p};if("a"===this.tag)R.on=k,R.attrs={href:c};else{var E=d(this.$slots.default);if(E){E.isStatic=!1;var O=Ot.util.extend;(E.data=O({},E.data)).on=k,(E.data.attrs=O({},E.data.attrs)).href=c}else R.on=k}return t(this.tag,R,this.$slots.default)}},Ht="undefined"!=typeof window,It=Array.isArray||function(t){return"[object Array]"==Object.prototype.toString.call(t)},Mt=$,Vt=b,zt=k,Bt=T,Ft=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");Mt.parse=Vt,Mt.compile=function(t,e){return k(b(t,e))},Mt.tokensToFunction=zt,Mt.tokensToRegExp=Bt;var Dt=Object.create(null),Kt=Object.create(null),Jt=Ht&&function(){var t=window.navigator.userAgent;return(-1===t.indexOf("Android 2.")&&-1===t.indexOf("Android 4.0")||-1===t.indexOf("Mobile Safari")||-1!==t.indexOf("Chrome")||-1!==t.indexOf("Windows Phone"))&&(window.history&&"pushState"in window.history)}(),Nt=Ht&&window.performance&&window.performance.now?window.performance:Date,Qt=W(),Xt=function(t,e){this.router=t,this.base=ut(e),this.current=qt,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[]};Xt.prototype.listen=function(t){this.cb=t},Xt.prototype.onReady=function(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))},Xt.prototype.onError=function(t){this.errorCbs.push(t)},Xt.prototype.transitionTo=function(t,e,r){var n=this,o=this.router.match(t,this.current);this.confirmTransition(o,function(){n.updateRoute(o),e&&e(o),n.ensureURL(),n.ready||(n.ready=!0,n.readyCbs.forEach(function(t){t(o)}))},function(t){r&&r(t),t&&!n.ready&&(n.ready=!0,n.readyErrorCbs.forEach(function(e){e(t)}))})},Xt.prototype.confirmTransition=function(r,n,o){var i=this,a=this.current,u=function(r){e(r)&&(i.errorCbs.length?i.errorCbs.forEach(function(t){t(r)}):(t(!1,"uncaught error during route navigation:"),console.error(r))),o&&o(r)};if(s(r,a)&&r.matched.length===a.matched.length)return this.ensureURL(),u();var c=ct(this.current.matched,r.matched),p=c.updated,f=c.deactivated,h=c.activated,l=[].concat(ft(f),this.router.beforeHooks,ht(p),h.map(function(t){return t.beforeEnter}),nt(h));this.pending=r;var d=function(t,n){if(i.pending!==r)return u();try{t(r,a,function(t){!1===t||e(t)?(i.ensureURL(!0),u(t)):"string"==typeof t||"object"==typeof t&&("string"==typeof t.path||"string"==typeof t.name)?(u(),"object"==typeof t&&t.replace?i.replace(t):i.push(t)):n(t)})}catch(t){u(t)}};rt(l,d,function(){var t=[];rt(dt(h,t,function(){return i.current===r}).concat(i.router.resolveHooks),d,function(){if(i.pending!==r)return u();i.pending=null,n(r),i.router.app&&i.router.app.$nextTick(function(){t.forEach(function(t){t()})})})})},Xt.prototype.updateRoute=function(t){var e=this.current;this.current=t,this.cb&&this.cb(t),this.router.afterHooks.forEach(function(r){r&&r(t,e)})};var Yt=function(t){function e(e,r){var n=this;t.call(this,e,r);var o=e.options.scrollBehavior;o&&B(),window.addEventListener("popstate",function(t){var r=n.current;n.transitionTo(mt(n.base),function(t){o&&F(e,t,r,!0)})})}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.go=function(t){window.history.go(t)},e.prototype.push=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){tt(g(n.base+t.fullPath)),F(n.router,t,o,!1),e&&e(t)},r)},e.prototype.replace=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){et(g(n.base+t.fullPath)),F(n.router,t,o,!1),e&&e(t)},r)},e.prototype.ensureURL=function(t){if(mt(this.base)!==this.current.fullPath){var e=g(this.base+this.current.fullPath);t?tt(e):et(e)}},e.prototype.getCurrentLocation=function(){return mt(this.base)},e}(Xt),Wt=function(t){function e(e,r,n){t.call(this,e,r),n&>(this.base)||bt()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.setupListeners=function(){var t=this;window.addEventListener("hashchange",function(){bt()&&t.transitionTo(wt(),function(t){kt(t.fullPath)})})},e.prototype.push=function(t,e,r){this.transitionTo(t,function(t){xt(t.fullPath),e&&e(t)},r)},e.prototype.replace=function(t,e,r){this.transitionTo(t,function(t){kt(t.fullPath),e&&e(t)},r)},e.prototype.go=function(t){window.history.go(t)},e.prototype.ensureURL=function(t){var e=this.current.fullPath;wt()!==e&&(t?xt(e):kt(e))},e.prototype.getCurrentLocation=function(){return wt()},e}(Xt),Gt=function(t){function e(e,r){t.call(this,e,r),this.stack=[],this.index=-1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.push=function(t,e,r){var n=this;this.transitionTo(t,function(t){n.stack=n.stack.slice(0,n.index+1).concat(t),n.index++,e&&e(t)},r)},e.prototype.replace=function(t,e,r){var n=this;this.transitionTo(t,function(t){n.stack=n.stack.slice(0,n.index).concat(t),e&&e(t)},r)},e.prototype.go=function(t){var e=this,r=this.index+t;if(!(r<0||r>=this.stack.length)){var n=this.stack[r];this.confirmTransition(n,function(){e.index=r,e.updateRoute(n)})}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:"/"},e.prototype.ensureURL=function(){},e}(Xt),Zt=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=M(t.routes||[],this);var e=t.mode||"hash";switch(this.fallback="history"===e&&!Jt&&!1!==t.fallback,this.fallback&&(e="hash"),Ht||(e="abstract"),this.mode=e,e){case"history":this.history=new Yt(this,t.base);break;case"hash":this.history=new Wt(this,t.base,this.fallback);break;case"abstract":this.history=new Gt(this,t.base)}},te={currentRoute:{}};return Zt.prototype.match=function(t,e,r){return this.matcher.match(t,e,r)},te.currentRoute.get=function(){return this.history&&this.history.current},Zt.prototype.init=function(t){var e=this;if(this.apps.push(t),!this.app){this.app=t;var r=this.history;if(r instanceof Yt)r.transitionTo(r.getCurrentLocation());else if(r instanceof Wt){var n=function(){r.setupListeners()};r.transitionTo(r.getCurrentLocation(),n,n)}r.listen(function(t){e.apps.forEach(function(e){e._route=t})})}},Zt.prototype.beforeEach=function(t){return Rt(this.beforeHooks,t)},Zt.prototype.beforeResolve=function(t){return Rt(this.resolveHooks,t)},Zt.prototype.afterEach=function(t){return Rt(this.afterHooks,t)},Zt.prototype.onReady=function(t,e){this.history.onReady(t,e)},Zt.prototype.onError=function(t){this.history.onError(t)},Zt.prototype.push=function(t,e,r){this.history.push(t,e,r)},Zt.prototype.replace=function(t,e,r){this.history.replace(t,e,r)},Zt.prototype.go=function(t){this.history.go(t)},Zt.prototype.back=function(){this.go(-1)},Zt.prototype.forward=function(){this.go(1)},Zt.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map(function(t){return Object.keys(t.components).map(function(e){return t.components[e]})})):[]},Zt.prototype.resolve=function(t,e,r){var n=H(t,e||this.history.current,r,this),o=this.match(n,e),i=o.redirectedFrom||o.fullPath;return{location:n,route:o,href:Et(this.history.base,i,this.mode),normalizedTo:n,resolved:o}},Zt.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==qt&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(Zt.prototype,te),Zt.install=y,Zt.version="2.7.0",Ht&&window.Vue&&window.Vue.use(Zt),Zt}); \ No newline at end of file +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueRouter=e()}(this,function(){"use strict";function t(t,e){}function e(t){return Object.prototype.toString.call(t).indexOf("Error")>-1}function r(t,e){switch(typeof e){case"undefined":return;case"object":return e;case"function":return e(t);case"boolean":return e?t.params:void 0}}function n(t,e,r){void 0===e&&(e={});var n,i=r||o;try{n=i(t||"")}catch(t){n={}}for(var a in e)n[a]=e[a];return n}function o(t){var e={};return(t=t.trim().replace(/^(\?|#|&)/,""))?(t.split("&").forEach(function(t){var r=t.replace(/\+/g," ").split("="),n=Pt(r.shift()),o=r.length>0?Pt(r.join("=")):null;void 0===e[n]?e[n]=o:Array.isArray(e[n])?e[n].push(o):e[n]=[e[n],o]}),e):e}function i(t){var e=t?Object.keys(t).map(function(e){var r=t[e];if(void 0===r)return"";if(null===r)return Lt(e);if(Array.isArray(r)){var n=[];return r.forEach(function(t){void 0!==t&&(null===t?n.push(Lt(e)):n.push(Lt(e)+"="+Lt(t)))}),n.join("&")}return Lt(e)+"="+Lt(r)}).filter(function(t){return t.length>0}).join("&"):null;return e?"?"+e:""}function a(t,e,r,n){var o=n&&n.options.stringifyQuery,i=e.query||{};try{i=u(i)}catch(t){}var a={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||"/",hash:e.hash||"",query:i,params:e.params||{},fullPath:s(e,o),matched:t?c(t):[]};return r&&(a.redirectedFrom=s(r,o)),Object.freeze(a)}function u(t){if(Array.isArray(t))return t.map(u);if(t&&"object"==typeof t){var e={};for(var r in t)e[r]=u(t[r]);return e}return t}function c(t){for(var e=[];t;)e.unshift(t),t=t.parent;return e}function s(t,e){var r=t.path,n=t.query;void 0===n&&(n={});var o=t.hash;void 0===o&&(o="");var a=e||i;return(r||"/")+a(n)+o}function p(t,e){return e===Mt?t===e:!!e&&(t.path&&e.path?t.path.replace(Ut,"")===e.path.replace(Ut,"")&&t.hash===e.hash&&f(t.query,e.query):!(!t.name||!e.name)&&(t.name===e.name&&t.hash===e.hash&&f(t.query,e.query)&&f(t.params,e.params)))}function f(t,e){if(void 0===t&&(t={}),void 0===e&&(e={}),!t||!e)return t===e;var r=Object.keys(t),n=Object.keys(e);return r.length===n.length&&r.every(function(r){var n=t[r],o=e[r];return"object"==typeof n&&"object"==typeof o?f(n,o):String(n)===String(o)})}function h(t,e){return 0===t.path.replace(Ut,"/").indexOf(e.path.replace(Ut,"/"))&&(!e.hash||t.hash===e.hash)&&l(t.query,e.query)}function l(t,e){for(var r in e)if(!(r in t))return!1;return!0}function d(t){if(!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey||t.defaultPrevented||void 0!==t.button&&0!==t.button)){if(t.currentTarget&&t.currentTarget.getAttribute){var e=t.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(e))return}return t.preventDefault&&t.preventDefault(),!0}}function y(t){if(t)for(var e,r=0;r=0&&(e=t.slice(n),t=t.slice(0,n));var o=t.indexOf("?");return o>=0&&(r=t.slice(o+1),t=t.slice(0,o)),{path:t,query:r,hash:e}}function b(t){return t.replace(/\/\//g,"/")}function w(t,e){for(var r,n=[],o=0,i=0,a="",u=e&&e.delimiter||"/";null!=(r=Nt.exec(t));){var c=r[0],s=r[1],p=r.index;if(a+=t.slice(i,p),i=p+c.length,s)a+=s[1];else{var f=t[i],h=r[2],l=r[3],d=r[4],y=r[5],v=r[6],m=r[7];a&&(n.push(a),a="");var g=null!=h&&null!=f&&f!==h,b="+"===v||"*"===v,w="?"===v||"*"===v,x=r[2]||u,k=d||y;n.push({name:l||o++,prefix:h||"",delimiter:x,optional:w,repeat:b,partial:g,asterisk:!!m,pattern:k?O(k):m?".*":"[^"+E(x)+"]+?"})}}return i-1&&(o.params[h]=r.params[h]);if(u)return o.path=q(u.path,o.params,'named route "'+a+'"'),i(u,o,n)}else if(o.path){o.params={};for(var l=0;l=t.length?r():t[o]?e(t[o],function(){n(o+1)}):n(o+1)};n(0)}function it(t){return function(r,n,o){var i=!1,a=0,u=null;at(t,function(t,r,n,c){if("function"==typeof t&&void 0===t.cid){i=!0,a++;var s,p=st(function(e){ct(e)&&(e=e.default),t.resolved="function"==typeof e?e:_t.extend(e),n.components[c]=e,--a<=0&&o()}),f=st(function(t){var r="Failed to resolve async component "+c+": "+t;u||(u=e(t)?t:new Error(r),o(u))});try{s=t(p,f)}catch(t){f(t)}if(s)if("function"==typeof s.then)s.then(p,f);else{var h=s.component;h&&"function"==typeof h.then&&h.then(p,f)}}}),i||o()}}function at(t,e){return ut(t.map(function(t){return Object.keys(t.components).map(function(r){return e(t.components[r],t.instances[r],t,r)})}))}function ut(t){return Array.prototype.concat.apply([],t)}function ct(t){return t.__esModule||Zt&&"Module"===t[Symbol.toStringTag]}function st(t){var e=!1;return function(){for(var r=[],n=arguments.length;n--;)r[n]=arguments[n];if(!e)return e=!0,t.apply(this,r)}}function pt(t){if(!t)if(zt){var e=document.querySelector("base");t=(t=e&&e.getAttribute("href")||"/").replace(/^https?:\/\/[^\/]+/,"")}else t="/";return"/"!==t.charAt(0)&&(t="/"+t),t.replace(/\/$/,"")}function ft(t,e){var r,n=Math.max(t.length,e.length);for(r=0;r=0?e.slice(0,r):e)+"#"+t}function Ot(t){Yt?rt(Et(t)):window.location.hash=t}function Ct(t){Yt?nt(Et(t)):window.location.replace(Et(t))}function jt(t,e){return t.push(e),function(){var r=t.indexOf(e);r>-1&&t.splice(r,1)}}function At(t,e,r){var n="hash"===r?"#"+e:e;return t?b(t+"/"+n):n}var _t,Tt={name:"router-view",functional:!0,props:{name:{type:String,default:"default"}},render:function(t,e){var n=e.props,o=e.children,i=e.parent,a=e.data;a.routerView=!0;for(var u=i.$createElement,c=n.name,s=i.$route,p=i._routerViewCache||(i._routerViewCache={}),f=0,h=!1;i&&i._routerRoot!==i;)i.$vnode&&i.$vnode.data.routerView&&f++,i._inactive&&(h=!0),i=i.$parent;if(a.routerViewDepth=f,h)return u(p[c],a,o);var l=s.matched[f];if(!l)return p[c]=null,u();var d=p[c]=l.components[c];a.registerRouteInstance=function(t,e){var r=l.instances[c];(e&&r!==t||!e&&r===t)&&(l.instances[c]=e)},(a.hook||(a.hook={})).prepatch=function(t,e){l.instances[c]=e.componentInstance},a.props=r(s,l.props&&l.props[c]),a.attrs={};for(var y in a.props)"props"in d&&y in d.props||(a.attrs[y]=a.props[y],delete a.props[y]);return u(d,a,o)}},St=/[!'()*]/g,$t=function(t){return"%"+t.charCodeAt(0).toString(16)},qt=/%2C/g,Lt=function(t){return encodeURIComponent(t).replace(St,$t).replace(qt,",")},Pt=decodeURIComponent,Ut=/\/?$/,Mt=a(null,{path:"/"}),Ht=[String,Object],It=[String,Array],Vt={name:"router-link",props:{to:{type:Ht,required:!0},tag:{type:String,default:"a"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,event:{type:It,default:"click"}},render:function(t){var e=this,r=this.$router,n=this.$route,o=r.resolve(this.to,n,this.append),i=o.location,u=o.route,c=o.href,s={},f=r.options.linkActiveClass,l=r.options.linkExactActiveClass,v=null==f?"router-link-active":f,m=null==l?"router-link-exact-active":l,g=null==this.activeClass?v:this.activeClass,b=null==this.exactActiveClass?m:this.exactActiveClass,w=i.path?a(null,i,null,r):u;s[b]=p(n,w),s[g]=this.exact?s[b]:h(n,w);var x=function(t){d(t)&&(e.replace?r.replace(i):r.push(i))},k={click:d};Array.isArray(this.event)?this.event.forEach(function(t){k[t]=x}):k[this.event]=x;var R={class:s};if("a"===this.tag)R.on=k,R.attrs={href:c};else{var E=y(this.$slots.default);if(E){E.isStatic=!1;var O=_t.util.extend;(E.data=O({},E.data)).on=k,(E.data.attrs=O({},E.data.attrs)).href=c}else R.on=k}return t(this.tag,R,this.$slots.default)}},zt="undefined"!=typeof window,Bt=Array.isArray||function(t){return"[object Array]"==Object.prototype.toString.call(t)},Ft=$,Dt=w,Kt=R,Jt=S,Nt=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");Ft.parse=Dt,Ft.compile=function(t,e){return R(w(t,e))},Ft.tokensToFunction=Kt,Ft.tokensToRegExp=Jt;var Qt=Object.create(null),Xt=Object.create(null),Yt=zt&&function(){var t=window.navigator.userAgent;return(-1===t.indexOf("Android 2.")&&-1===t.indexOf("Android 4.0")||-1===t.indexOf("Mobile Safari")||-1!==t.indexOf("Chrome")||-1!==t.indexOf("Windows Phone"))&&(window.history&&"pushState"in window.history)}(),Wt=zt&&window.performance&&window.performance.now?window.performance:Date,Gt=Z(),Zt="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag,te=function(t,e){this.router=t,this.base=pt(e),this.current=Mt,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[]};te.prototype.listen=function(t){this.cb=t},te.prototype.onReady=function(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))},te.prototype.onError=function(t){this.errorCbs.push(t)},te.prototype.transitionTo=function(t,e,r){var n=this,o=this.router.match(t,this.current);this.confirmTransition(o,function(){n.updateRoute(o),e&&e(o),n.ensureURL(),n.ready||(n.ready=!0,n.readyCbs.forEach(function(t){t(o)}))},function(t){r&&r(t),t&&!n.ready&&(n.ready=!0,n.readyErrorCbs.forEach(function(e){e(t)}))})},te.prototype.confirmTransition=function(r,n,o){var i=this,a=this.current,u=function(r){e(r)&&(i.errorCbs.length?i.errorCbs.forEach(function(t){t(r)}):(t(!1,"uncaught error during route navigation:"),console.error(r))),o&&o(r)};if(p(r,a)&&r.matched.length===a.matched.length)return this.ensureURL(),u();var c=ft(this.current.matched,r.matched),s=c.updated,f=c.deactivated,h=c.activated,l=[].concat(dt(f),this.router.beforeHooks,yt(s),h.map(function(t){return t.beforeEnter}),it(h));this.pending=r;var d=function(t,n){if(i.pending!==r)return u();try{t(r,a,function(t){!1===t||e(t)?(i.ensureURL(!0),u(t)):"string"==typeof t||"object"==typeof t&&("string"==typeof t.path||"string"==typeof t.name)?(u(),"object"==typeof t&&t.replace?i.replace(t):i.push(t)):n(t)})}catch(t){u(t)}};ot(l,d,function(){var t=[];ot(mt(h,t,function(){return i.current===r}).concat(i.router.resolveHooks),d,function(){if(i.pending!==r)return u();i.pending=null,n(r),i.router.app&&i.router.app.$nextTick(function(){t.forEach(function(t){t()})})})})},te.prototype.updateRoute=function(t){var e=this.current;this.current=t,this.cb&&this.cb(t),this.router.afterHooks.forEach(function(r){r&&r(t,e)})};var ee=function(t){function e(e,r){var n=this;t.call(this,e,r);var o=e.options.scrollBehavior;o&&F();var i=wt(this.base);window.addEventListener("popstate",function(t){var r=n.current,a=wt(n.base);n.current===Mt&&a===i||n.transitionTo(a,function(t){o&&D(e,t,r,!0)})})}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.go=function(t){window.history.go(t)},e.prototype.push=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){rt(b(n.base+t.fullPath)),D(n.router,t,o,!1),e&&e(t)},r)},e.prototype.replace=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){nt(b(n.base+t.fullPath)),D(n.router,t,o,!1),e&&e(t)},r)},e.prototype.ensureURL=function(t){if(wt(this.base)!==this.current.fullPath){var e=b(this.base+this.current.fullPath);t?rt(e):nt(e)}},e.prototype.getCurrentLocation=function(){return wt(this.base)},e}(te),re=function(t){function e(e,r,n){t.call(this,e,r),n&&xt(this.base)||kt()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.setupListeners=function(){var t=this,e=this.router.options.scrollBehavior,r=Yt&&e;r&&F(),window.addEventListener(Yt?"popstate":"hashchange",function(){var e=t.current;kt()&&t.transitionTo(Rt(),function(n){r&&D(t.router,n,e,!0),Yt||Ct(n.fullPath)})})},e.prototype.push=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){Ot(t.fullPath),D(n.router,t,o,!1),e&&e(t)},r)},e.prototype.replace=function(t,e,r){var n=this,o=this.current;this.transitionTo(t,function(t){Ct(t.fullPath),D(n.router,t,o,!1),e&&e(t)},r)},e.prototype.go=function(t){window.history.go(t)},e.prototype.ensureURL=function(t){var e=this.current.fullPath;Rt()!==e&&(t?Ot(e):Ct(e))},e.prototype.getCurrentLocation=function(){return Rt()},e}(te),ne=function(t){function e(e,r){t.call(this,e,r),this.stack=[],this.index=-1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.push=function(t,e,r){var n=this;this.transitionTo(t,function(t){n.stack=n.stack.slice(0,n.index+1).concat(t),n.index++,e&&e(t)},r)},e.prototype.replace=function(t,e,r){var n=this;this.transitionTo(t,function(t){n.stack=n.stack.slice(0,n.index).concat(t),e&&e(t)},r)},e.prototype.go=function(t){var e=this,r=this.index+t;if(!(r<0||r>=this.stack.length)){var n=this.stack[r];this.confirmTransition(n,function(){e.index=r,e.updateRoute(n)})}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:"/"},e.prototype.ensureURL=function(){},e}(te),oe=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=V(t.routes||[],this);var e=t.mode||"hash";switch(this.fallback="history"===e&&!Yt&&!1!==t.fallback,this.fallback&&(e="hash"),zt||(e="abstract"),this.mode=e,e){case"history":this.history=new ee(this,t.base);break;case"hash":this.history=new re(this,t.base,this.fallback);break;case"abstract":this.history=new ne(this,t.base)}},ie={currentRoute:{configurable:!0}};return oe.prototype.match=function(t,e,r){return this.matcher.match(t,e,r)},ie.currentRoute.get=function(){return this.history&&this.history.current},oe.prototype.init=function(t){var e=this;if(this.apps.push(t),!this.app){this.app=t;var r=this.history;if(r instanceof ee)r.transitionTo(r.getCurrentLocation());else if(r instanceof re){var n=function(){r.setupListeners()};r.transitionTo(r.getCurrentLocation(),n,n)}r.listen(function(t){e.apps.forEach(function(e){e._route=t})})}},oe.prototype.beforeEach=function(t){return jt(this.beforeHooks,t)},oe.prototype.beforeResolve=function(t){return jt(this.resolveHooks,t)},oe.prototype.afterEach=function(t){return jt(this.afterHooks,t)},oe.prototype.onReady=function(t,e){this.history.onReady(t,e)},oe.prototype.onError=function(t){this.history.onError(t)},oe.prototype.push=function(t,e,r){this.history.push(t,e,r)},oe.prototype.replace=function(t,e,r){this.history.replace(t,e,r)},oe.prototype.go=function(t){this.history.go(t)},oe.prototype.back=function(){this.go(-1)},oe.prototype.forward=function(){this.go(1)},oe.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map(function(t){return Object.keys(t.components).map(function(e){return t.components[e]})})):[]},oe.prototype.resolve=function(t,e,r){var n=H(t,e||this.history.current,r,this),o=this.match(n,e),i=o.redirectedFrom||o.fullPath;return{location:n,route:o,href:At(this.history.base,i,this.mode),normalizedTo:n,resolved:o}},oe.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==Mt&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(oe.prototype,ie),oe.install=v,oe.version="2.8.0",zt&&window.Vue&&window.Vue.use(oe),oe}); \ No newline at end of file diff --git a/docs/book.json b/docs/book.json index 0a601db95..b8d2d762f 100644 --- a/docs/book.json +++ b/docs/book.json @@ -1,6 +1,7 @@ { + "title": "vue-router", "gitbook": ">3.0.0", - "plugins": ["edit-link", "theme-vuejs@git+https://github.com/pearofducks/gitbook-plugin-theme-vuejs.git", "-fontsettings", "github"], + "plugins": ["edit-link", "theme-vuejs", "-fontsettings", "github"], "pluginsConfig": { "edit-link": { "base": "https://github.com/vuejs/vue-router/edit/dev/docs", diff --git a/docs/en/advanced/lazy-loading.md b/docs/en/advanced/lazy-loading.md index a6465b094..d4c76c41e 100644 --- a/docs/en/advanced/lazy-loading.md +++ b/docs/en/advanced/lazy-loading.md @@ -2,7 +2,7 @@ When building apps with a bundler, the JavaScript bundle can become quite large, and thus affect the page load time. It would be more efficient if we can split each route's components into a separate chunk, and only load them when the route is visited. -Combining Vue's [async component feature](http://vuejs.org/guide/components.html#Async-Components) and webpack's [code splitting feature](https://webpack.js.org/guides/code-splitting-async/), it's trivially easy to lazy-load route components. +Combining Vue's [async component feature](https://vuejs.org/guide/components.html#Async-Components) and webpack's [code splitting feature](https://webpack.js.org/guides/code-splitting-async/), it's trivially easy to lazy-load route components. First, an async component can be defined as a factory function that returns a Promise (which should resolve to the component itself): @@ -16,7 +16,7 @@ Second, in webpack 2, we can use the [dynamic import](https://github.com/tc39/pr import('./Foo.vue') // returns a Promise ``` -> Note: if you are using Babel, you will need to add the [syntax-dynamic-import](http://babeljs.io/docs/plugins/syntax-dynamic-import/) plugin so that Babel can properly parse the syntax. +> Note: if you are using Babel, you will need to add the [syntax-dynamic-import](https://babeljs.io/docs/plugins/syntax-dynamic-import/) plugin so that Babel can properly parse the syntax. Combining the two, this is how to define an async component that will be automatically code-split by webpack: diff --git a/docs/en/advanced/navigation-guards.md b/docs/en/advanced/navigation-guards.md index 8ff4cc730..c479cde64 100644 --- a/docs/en/advanced/navigation-guards.md +++ b/docs/en/advanced/navigation-guards.md @@ -30,7 +30,7 @@ Every guard function receives three arguments: - **`next(false)`**: abort the current navigation. If the browser URL was changed (either manually by the user or via back button), it will be reset to that of the `from` route. - - **`next('/')` or `next({ path: '/' })`**: redirect to a different location. The current navigation will be aborted and a new one will be started. + - **`next('/')` or `next({ path: '/' })`**: redirect to a different location. The current navigation will be aborted and a new one will be started. You can pass any location object to `next`, which allows you to specify options like `replace: true`, `name: 'home'` and any option used in [`router-link`'s `to` prop](../api/router-link.md) or [`router.push`](../api/router-instance.md#methods) - **`next(error)`**: (2.4.0+) if the argument passed to `next` is an instance of `Error`, the navigation will be aborted and the error will be passed to callbacks registered via `router.onError()`. @@ -77,7 +77,7 @@ These guards have the exact same signature as global before guards. Finally, you can directly define route navigation guards inside route components (the ones passed to the router configuration) with the following options: - `beforeRouteEnter` -- `beforeRouteUpdate` (added in 2.2) +- `beforeRouteUpdate` (added in 2.2+) - `beforeRouteLeave` ``` js @@ -91,8 +91,8 @@ const Foo = { beforeRouteUpdate (to, from, next) { // called when the route that renders this component has changed, // but this component is reused in the new route. - // For example, for a route with dynamic params /foo/:id, when we - // navigate between /foo/1 and /foo/2, the same Foo component instance + // For example, for a route with dynamic params `/foo/:id`, when we + // navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance // will be reused, and this hook will be called when that happens. // has access to `this` component instance. }, @@ -116,18 +116,39 @@ beforeRouteEnter (to, from, next) { } ``` -You can directly access `this` inside `beforeRouteLeave`. The leave guard is usually used to prevent the user from accidentally leaving the route with unsaved edits. The navigation can be canceled by calling `next(false)`. +Note that `beforeRouteEnter` is the only hook that supports passing a callback to `next`. For `beforeRouteUpdate` and `beforeRouteLeave`, `this` is already available, so passing a callback is unnecessary and therefore *not supported*: + +```js +beforeRouteUpdate (to, from, next) { + // just use `this` + this.name = to.params.name + next() +} +``` + +The **leave guard** is usually used to prevent the user from accidentally leaving the route with unsaved edits. The navigation can be canceled by calling `next(false)`. + +```js +beforeRouteLeave (to, from , next) { + const answer = window.confirm('Do you really want to leave? you have unsaved changes!') + if (answer) { + next() + } else { + next(false) + } +} +``` ### The Full Navigation Resolution Flow -1. Navigation triggered -2. Call leave guards in deactivated components -3. Call global `beforeEach` guards -4. Call `beforeRouteUpdate` guards in reused components (2.2+) -5. Call `beforeEnter` in route configs -6. Resolve async route components -7. Call `beforeRouteEnter` in activated components -8. Call global `beforeResolve` guards (2.5+) +1. Navigation triggered. +2. Call leave guards in deactivated components. +3. Call global `beforeEach` guards. +4. Call `beforeRouteUpdate` guards in reused components (2.2+). +5. Call `beforeEnter` in route configs. +6. Resolve async route components. +7. Call `beforeRouteEnter` in activated components. +8. Call global `beforeResolve` guards (2.5+). 9. Navigation confirmed. 10. Call global `afterEach` hooks. 11. DOM updates triggered. diff --git a/docs/en/advanced/scroll-behavior.md b/docs/en/advanced/scroll-behavior.md index 99d468991..a9281ac40 100644 --- a/docs/en/advanced/scroll-behavior.md +++ b/docs/en/advanced/scroll-behavior.md @@ -2,7 +2,7 @@ When using client-side routing, we may want to scroll to top when navigating to a new route, or preserve the scrolling position of history entries just like real page reload does. `vue-router` allows you to achieve these and even better, allows you to completely customize the scroll behavior on route navigation. -**Note: this feature only works in HTML5 history mode.** +**Note: this feature only works if the browser supports `history.pushState`.** When creating the router instance, you can provide the `scrollBehavior` function: @@ -60,3 +60,21 @@ scrollBehavior (to, from, savedPosition) { ``` We can also use [route meta fields](meta.md) to implement fine-grained scroll behavior control. Check out a full example [here](https://github.com/vuejs/vue-router/blob/dev/examples/scroll-behavior/app.js). + +### Async Scrolling + +> New in 2.8.0 + +You can also return a Promise that resolves to the desired position descriptor: + +``` js +scrollBehavior (to, from, savedPosition) { + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve({ x: 0, y: 0 }) + }, 500) + }) +} +``` + +It's possible to hook this up with events from a page-level transition component to make the scroll behavior play nicely with your page transitions, but due to the possible variance and complexity in use cases, we simply provide this primitive to enable specific userland implementations. diff --git a/docs/en/advanced/transitions.md b/docs/en/advanced/transitions.md index 495f6bee2..9621c14e5 100644 --- a/docs/en/advanced/transitions.md +++ b/docs/en/advanced/transitions.md @@ -8,7 +8,7 @@ Since the `` is essentially a dynamic component, we can apply trans ``` -[Everything about ``](http://vuejs.org/guide/transitions.html) works the same here. +[Everything about ``](https://vuejs.org/guide/transitions.html) works the same here. ### Per-Route Transition diff --git a/docs/en/api/options.md b/docs/en/api/options.md index 020feeec8..2a65792d5 100644 --- a/docs/en/api/options.md +++ b/docs/en/api/options.md @@ -74,11 +74,16 @@ Signature: ``` - ( + type PositionDescriptor = + { x: number, y: number } | + { selector: string } | + ?{} + + type scrollBehaviorHandler = ( to: Route, from: Route, savedPosition?: { x: number, y: number } - ) => { x: number, y: number } | { selector: string } | ?{} + ) => PositionDescriptor | Promise ``` For more details see [Scroll Behavior](../advanced/scroll-behavior.md). diff --git a/docs/en/api/router-link.md b/docs/en/api/router-link.md index 82c49770a..c218c5d0d 100644 --- a/docs/en/api/router-link.md +++ b/docs/en/api/router-link.md @@ -42,7 +42,6 @@ Register ``` - - **replace** - type: `boolean` @@ -55,7 +54,6 @@ ``` - - **append** - type: `boolean` @@ -68,7 +66,6 @@ ``` - - **tag** - type: `string` @@ -83,7 +80,6 @@
  • foo
  • ``` - - **active-class** - type: `string` @@ -107,7 +103,7 @@ ``` - Checkout more examples explaining active link class [live](https://jsfiddle.net/8xrk1n9f/). + Check out more examples explaining active link class [live](https://jsfiddle.net/8xrk1n9f/). - **event** diff --git a/docs/en/essentials/dynamic-matching.md b/docs/en/essentials/dynamic-matching.md index 6107b5f9a..1e235377c 100644 --- a/docs/en/essentials/dynamic-matching.md +++ b/docs/en/essentials/dynamic-matching.md @@ -25,7 +25,7 @@ const User = { } ``` -You can checkout a live example [here](http://jsfiddle.net/yyx990803/4xfa2f19/). +You can check out a live example [here](https://jsfiddle.net/yyx990803/4xfa2f19/). You can have multiple dynamic segments in the same route, and they will map to corresponding fields on `$route.params`. Examples: diff --git a/docs/en/essentials/getting-started.md b/docs/en/essentials/getting-started.md index a6a7e9e24..8b826aa12 100644 --- a/docs/en/essentials/getting-started.md +++ b/docs/en/essentials/getting-started.md @@ -2,7 +2,7 @@ > We will be using [ES2015](https://github.com/lukehoban/es6features) in the code samples in the guide. -Creating a Single-page Application with Vue.js + vue-router is dead simple. With Vue.js, we are already composing our application with components. When adding vue-router to the mix, all we need to do is map our components to the routes and let vue-router know where to render them. Here's a basic example: +Creating a Single-page Application with Vue + Vue Router is dead simple. With Vue.js, we are already composing our application with components. When adding vue-router to the mix, all we need to do is map our components to the routes and let vue-router know where to render them. Here's a basic example: > All examples will be using the full version of Vue to make template parsing possible. See more details [here](https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only). @@ -30,7 +30,8 @@ Creating a Single-page Application with Vue.js + vue-router is dead simple. With ### JavaScript ``` js -// 0. If using a module system (e.g. via vue-cli), import Vue and VueRouter and then call `Vue.use(VueRouter)`. +// 0. If using a module system (e.g. via vue-cli), import Vue and VueRouter +// and then call `Vue.use(VueRouter)`. // 1. Define route components. // These can be imported from other files @@ -64,6 +65,29 @@ const app = new Vue({ // Now the app has started! ``` -You can also checkout this example [live](http://jsfiddle.net/yyx990803/xgrjzsup/). +By injecting the router, we get access to it as `this.$router` as well as the current route as `this.$route` inside of any component: + +```js +// Home.vue +export default { + computed: { + username () { + // We will see what `params` is shortly + return this.$route.params.username + } + }, + methods: { + goBack () { + window.history.length > 1 + ? this.$router.go(-1) + : this.$router.push('/') + } + } +} +``` + +Throughout the docs, we will often use the `router` instance. Keep in mind that `this.$router` is exactly the same as using `router`. The reason we use `this.$router` is because we don't want to import the router in every single component that needs to manipulate routing. + +You can also check out this example [live](http://jsfiddle.net/yyx990803/xgrjzsup/). Notice that a `` automatically gets the `.router-link-active` class when its target route is matched. You can learn more about it in its [API reference](../api/router-link.md). diff --git a/docs/en/essentials/history-mode.md b/docs/en/essentials/history-mode.md index ad1702e4f..6b23ab58f 100644 --- a/docs/en/essentials/history-mode.md +++ b/docs/en/essentials/history-mode.md @@ -15,7 +15,7 @@ When using history mode, the URL will look "normal," e.g. `http://oursite.com/us Here comes a problem, though: Since our app is a single page client side app, without a proper server configuration, the users will get a 404 error if they access `http://oursite.com/user/id` directly in their browser. Now that's ugly. -Not to worry: To fix the issue, all you need to do is add a simple catch-all fallback route to your server. If the URL doesn't match any static assets, it should serve the same `index.html` page that your app lives in. Beautiful, again! +Not to worry: To fix the issue, all you need to do is add a simple catch-all fallback route to your server. If the URL doesn't match any static assets, it should serve the same `index.html` page that your app lives in. Beautiful, again! ## Example Server Configurations @@ -40,12 +40,40 @@ location / { } ``` -#### Node.js (Express) +#### Native Node.js + +```js +const http = require('http') +const fs = require('fs') +const httpPort = 80 + +http.createServer((req, res) => { + fs.readFile('index.htm', 'utf-8', (err, content) => { + if (err) { +      console.log('We cannot open "index.htm" file.') + } + + res.writeHead(200, { + 'Content-Type': 'text/html; charset=utf-8' + }) + + res.end(content) + }) +}).listen(httpPort, () => { + console.log('Server listening on: http://localhost:%s', httpPort) +}) +``` + +#### Express with Node.js For Node.js/Express, consider using [connect-history-api-fallback middleware](https://github.com/bripkens/connect-history-api-fallback). #### Internet Information Services (IIS) -``` + +1. Install [IIS UrlRewrite](https://www.iis.net/downloads/microsoft/url-rewrite) +2. Create a `web.config` file in the root directory of your site with the following: + +```xml @@ -57,21 +85,41 @@ For Node.js/Express, consider using [connect-history-api-fallback middleware](ht - + - - - - - - - ``` +#### Caddy + +``` +rewrite { + regexp .* + to {path} / +} +``` + +#### Firebase hosting + +Add this to your `firebase.json`: + +``` +{ + "hosting": { + "public": "dist", + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } +} +``` + ## Caveat There is a caveat to this: Your server will no longer report 404 errors as all not-found paths now serve up your `index.html` file. To get around the issue, you should implement a catch-all route within your Vue app to show a 404 page: @@ -85,4 +133,4 @@ const router = new VueRouter({ }) ``` -Alternatively, if you are using a Node.js server, you can implement the fallback by using the router on the server side to match the incoming URL and respond with 404 if no route is matched. +Alternatively, if you are using a Node.js server, you can implement the fallback by using the router on the server side to match the incoming URL and respond with 404 if no route is matched. Check out the [Vue server side rendering documentation](https://ssr.vuejs.org/en/) for more information. diff --git a/docs/en/essentials/navigation.md b/docs/en/essentials/navigation.md index d2a08eb99..e48549ad5 100644 --- a/docs/en/essentials/navigation.md +++ b/docs/en/essentials/navigation.md @@ -30,8 +30,22 @@ router.push({ name: 'user', params: { userId: 123 }}) router.push({ path: 'register', query: { plan: 'private' }}) ``` +**Note**: `params` are ignored if a `path` is provided, which is not the case for `query`, as shown in the example above. Instead, you need to provide the `name` of the route or manually specify the whole `path` with any parameter: + +```js +const userId = 123 +router.push({ name: 'user', params: { userId }}) // -> /user/123 +router.push({ path: `/user/${userId}` }) // -> /user/123 +// This will NOT work +router.push({ path: '/user', params: { userId }}) // -> /user +``` + +The same rules apply for the `to` property of the `router-link` component. + In 2.2.0+, optionally provide `onComplete` and `onAbort` callbacks to `router.push` or `router.replace` as the 2nd and 3rd arguments. These callbacks will be called when the navigation either successfully completed (after all async hooks are resolved), or aborted (navigated to the same route, or to a different route before current navigation has finished), respectively. +**Note:** If the destination is the same as the current route and only params are changing (e.g. going from one profile to another `/users/1` -> `/users/2`), you will have to use [`beforeRouteUpdate`](./dynamic-matching.html#reacting-to-params-changes) to react to changes (e.g. fetching the user information). + #### `router.replace(location, onComplete?, onAbort?)` It acts like `router.push`, the only difference is that it navigates without pushing a new history entry, as its name suggests - it replaces the current entry. @@ -40,7 +54,6 @@ It acts like `router.push`, the only difference is that it navigates without pus |-------------|--------------| | `` | `router.replace(...)` | - #### `router.go(n)` This method takes a single integer as parameter that indicates by how many steps to go forwards or go backwards in the history stack, similar to `window.history.go(n)`. diff --git a/docs/en/essentials/nested-routes.md b/docs/en/essentials/nested-routes.md index 35241f0f4..8242d7526 100644 --- a/docs/en/essentials/nested-routes.md +++ b/docs/en/essentials/nested-routes.md @@ -48,8 +48,7 @@ const User = { } ``` -To render components into this nested outlet, we need to use the `children` -option in `VueRouter` constructor config: +To render components into this nested outlet, we need to use the `children` option in `VueRouter` constructor config: ``` js const router = new VueRouter({ @@ -97,4 +96,4 @@ const router = new VueRouter({ }) ``` -A working demo of this example can be found [here](http://jsfiddle.net/yyx990803/L7hscd8h/). +A working demo of this example can be found [here](https://jsfiddle.net/yyx990803/L7hscd8h/). diff --git a/docs/en/essentials/passing-props.md b/docs/en/essentials/passing-props.md index c50483d9d..3e2f6dbcf 100644 --- a/docs/en/essentials/passing-props.md +++ b/docs/en/essentials/passing-props.md @@ -1,10 +1,10 @@ # Passing Props to Route Components -Using `$route` in your component creates a tight coupling with the route which limits the flexibility of the component as it can only be used on certain urls. +Using `$route` in your component creates a tight coupling with the route which limits the flexibility of the component as it can only be used on certain URLs. -To decouple this component from the router use props: +To decouple this component from the router use option `props`: -**❌ Coupled to $route** +**❌ Coupled to `$route`** ``` js const User = { @@ -17,7 +17,7 @@ const router = new VueRouter({ }) ``` -**👍 Decoupled with props** +**👍 Decoupled with `props`** ``` js const User = { @@ -27,10 +27,10 @@ const User = { const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, props: true } - - // for routes with named views, you have to define the props option for each named view: + + // for routes with named views, you have to define the `props` option for each named view: { - path: '/user/:id', + path: '/user/:id', components: { default: User, sidebar: Sidebar }, props: { default: true, sidebar: false } } @@ -42,12 +42,11 @@ This allows you to use the component anywhere, which makes the component easier ### Boolean mode -When props is set to true, the route.params will be set as the component props. +When `props` is set to `true`, the `route.params` will be set as the component props. ### Object mode -When props is an object, this will be set as the component props as-is. -Useful for when the props are static. +When `props` is an object, this will be set as the component props as-is. Useful for when the props are static. ``` js const router = new VueRouter({ @@ -59,8 +58,7 @@ const router = new VueRouter({ ### Function mode -You can create a function that returns props. -This allows you to cast the parameter to another type, combine static values with route-based values, etc. +You can create a function that returns props. This allows you to cast parameters into other types, combine static values with route-based values, etc. ``` js const router = new VueRouter({ @@ -70,10 +68,8 @@ const router = new VueRouter({ }) ``` -The url: `/search?q=vue` would pass `{query: "vue"}` as props to the SearchUser component. - -Try to keep the props function stateless, as it's only evaluated on route changes. -Use a wrapper component if you need state to define the props, that way vue can react to state changes. +The URL `/search?q=vue` would pass `{query: 'vue'}` as props to the `SearchUser` component. +Try to keep the `props` function stateless, as it's only evaluated on route changes. Use a wrapper component if you need state to define the props, that way vue can react to state changes. -For advanced usage, checkout the [example](https://github.com/vuejs/vue-router/blob/dev/examples/route-props/app.js). +For advanced usage, check out the [example](https://github.com/vuejs/vue-router/blob/dev/examples/route-props/app.js). diff --git a/docs/en/essentials/redirect-and-alias.md b/docs/en/essentials/redirect-and-alias.md index b23aa95d7..859549bb6 100644 --- a/docs/en/essentials/redirect-and-alias.md +++ b/docs/en/essentials/redirect-and-alias.md @@ -35,6 +35,8 @@ const router = new VueRouter({ }) ``` +Note that [Navigation Guards](../advanced/navigation-guards.md) are not applied on the route that redirects, only on its target. In the example below, adding a `beforeEnter` or `beforeLeave` guard to the `/a` route would not have any effect. + For other advanced usage, checkout the [example](https://github.com/vuejs/vue-router/blob/dev/examples/redirect/app.js). ### Alias @@ -55,4 +57,4 @@ const router = new VueRouter({ An alias gives you the freedom to map a UI structure to an arbitrary URL, instead of being constrained by the configuration's nesting structure. -For advanced usage, checkout the [example](https://github.com/vuejs/vue-router/blob/dev/examples/route-alias/app.js). +For advanced usage, check out the [example](https://github.com/vuejs/vue-router/blob/dev/examples/route-alias/app.js). diff --git a/docs/en/installation.md b/docs/en/installation.md index b22fba8d4..3fae409a3 100644 --- a/docs/en/installation.md +++ b/docs/en/installation.md @@ -5,7 +5,7 @@ [https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js) -[Unpkg.com](https://unpkg.com) provides NPM-based CDN links. The above link will always point to the latest release on NPM. You can also use a specific version/tag via URLs like `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js`. +[Unpkg.com](https://unpkg.com) provides npm-based CDN links. The above link will always point to the latest release on npm. You can also use a specific version/tag via URLs like `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js`. Include `vue-router` after Vue and it will install itself automatically: @@ -15,7 +15,7 @@ Include `vue-router` after Vue and it will install itself automatically: ``` -### NPM +### npm ``` bash npm install vue-router diff --git a/docs/fr/SUMMARY.md b/docs/fr/SUMMARY.md index 49acb21bf..c601ba47b 100644 --- a/docs/fr/SUMMARY.md +++ b/docs/fr/SUMMARY.md @@ -2,7 +2,7 @@ > Note: vue-router@2.x fonctionne uniquement avec Vue 2.x. La doc pour la 0.7.x est [ici](https://github.com/vuejs/vue-router/tree/1.0/docs/en). -**[Notes de release](https://github.com/vuejs/vue-router/releases)** +**[Notes de version](https://github.com/vuejs/vue-router/releases)** - [Installation](installation.md) - Essentiel @@ -13,16 +13,16 @@ - [Routes nommées](essentials/named-routes.md) - [Vues nommées](essentials/named-views.md) - [Redirection et alias](essentials/redirect-and-alias.md) - - [Passing Props to Route Components (En)](essentials/passing-props.md) - - [HTML5 History Mode (En)](essentials/history-mode.md) + - [Passage de props aux composants de route](essentials/passing-props.md) + - [Mode historique de HTML5](essentials/history-mode.md) - Avancé - - [Navigation Guards (En)](advanced/navigation-guards.md) - - [Route Meta Fields (En)](advanced/meta.md) - - [Transitions (En)](advanced/transitions.md) + - [Intercepteurs de navigation](advanced/navigation-guards.md) + - [Champs meta de route](advanced/meta.md) + - [Transitions](advanced/transitions.md) - [Récupération de données](advanced/data-fetching.md) - [Comportement du défilement](advanced/scroll-behavior.md) - [Chargement à la volée](advanced/lazy-loading.md) -- Réference de l'API +- Référence de l'API - [Options de construction du routeur](api/options.md) - [routes](api/options.md#routes) - [mode](api/options.md#mode) diff --git a/docs/fr/advanced/data-fetching.md b/docs/fr/advanced/data-fetching.md index 98f3bea71..e34f83535 100644 --- a/docs/fr/advanced/data-fetching.md +++ b/docs/fr/advanced/data-fetching.md @@ -2,9 +2,9 @@ Parfois vous avez besoin de récupérer des données depuis le serveur lorsqu'une route est activée. Par exemple, avant de faire le rendu d'un profil utilisateur, vous avez besoin de récupérer les données de l'utilisateur depuis le serveur. Nous pouvons y parvenir de deux façons différentes : -- **Récupération de donnée après la navigation** : effectue la navigation en premier, et récupère les données dans le hook entrant du cycle de vie d'un composant. Affiche un état de chargement pendant que les données sont en train d'être récupérées. +- **Récupération de données après la navigation** : effectue la navigation en premier, et récupère les données dans le hook entrant du cycle de vie d'un composant. Affiche un état de chargement pendant que les données sont en train d'être récupérées. -- **Récupération de donnée avant la navigation** : récupère les données avant la navigation dans la fonction de sécurisation d'entrée de la route, et effectue la navigation après que les données aient été récupérées. +- **Récupération de données avant la navigation** : récupère les données avant la navigation dans la fonction d'interception d'entrée de la route, et effectue la navigation après que les données aient été récupérées. Techniquement, les deux choix sont valides. Cela dépend de l'expérience utilisateur que vous souhaitez apporter. @@ -71,7 +71,7 @@ export default { ## Récupération de données avant la navigation -Avec cette approche, nous récupérerons les données avant de naviguer vers la nouvelle route. Nous pouvons effectuer la récupération de données dans la fonction de sécurisation `beforeRouteEnter` du composant à venir, et seulement appeler `next` lorsque la récupération est terminée : +Avec cette approche, nous récupèrerons les données avant de naviguer vers la nouvelle route. Nous pouvons effectuer la récupération de données dans la fonction d'interception `beforeRouteEnter` du composant à venir, et seulement appeler `next` lorsque la récupération est terminée : ``` js export default { diff --git a/docs/fr/advanced/lazy-loading.md b/docs/fr/advanced/lazy-loading.md index 97c097e27..99582d2d9 100644 --- a/docs/fr/advanced/lazy-loading.md +++ b/docs/fr/advanced/lazy-loading.md @@ -2,7 +2,7 @@ Pendant la construction d'applications avec un empaqueteur (« bundler »), le paquetage JavaScript peut devenir un peu lourd, et donc cela peut affecter le temps de chargement de la page. Il serait plus efficace si l'on pouvait séparer chaque composant de route dans des fragments séparés, et de les charger uniquement lorsque la route est visitée. -En combinant la [fonctionnalité de composant asynchrone](https://fr.vuejs.org/v2/guide/components.html#Composants-asynchrones) de Vue et la [fonctionnalité de séparation de code](https://webpack.js.org/guides/code-splitting-async/) de webpack, il est très facile de charger à la volée les composants de route. +En combinant la [fonctionnalité de composant asynchrone](https://fr.vuejs.org/v2/guide/components.html#Composants-asynchrones) de Vue et la [fonctionnalité de scission de code](https://webpack.js.org/guides/code-splitting-async/) de webpack, il est très facile de charger à la volée les composants de route. Premièrement, un composant asynchrone peut définir une fonction fabrique qui retourne une Promesse (qui devrait résoudre le composant lui-même) : @@ -16,9 +16,9 @@ Deuxièmement, avec webpack 2, nous pouvons utiliser la syntaxe d'[import dynami import('./Foo.vue') // returns a Promise ``` -> Note: if you are using Babel, you will need to add the [syntax-dynamic-import](http://babeljs.io/docs/plugins/syntax-dynamic-import/) plugin so that Babel can properly parse the syntax. +> Note: si vous utilisez Babel, vous aurez besoin d'ajouter le plugin [syntax-dynamic-import](http://babeljs.io/docs/plugins/syntax-dynamic-import/) de façon à ce que Babel puisse analyser correctement la syntaxe. -Combining the two, this is how to define an async component that will be automatically code-split by webpack: +En combinant les deux, on définit un composant asynchrone qui sera automatiquement scindé par webpack : ``` js const Foo = () => import('./Foo.vue') diff --git a/docs/fr/advanced/meta.md b/docs/fr/advanced/meta.md index 998b6c134..24dc5ccd0 100644 --- a/docs/fr/advanced/meta.md +++ b/docs/fr/advanced/meta.md @@ -1,6 +1,6 @@ -# Route Meta Fields (En)

    *Cette page est en cours de traduction française. Revenez une autre fois pour lire une traduction achevée ou [participez à la traduction française ici](https://github.com/vuejs-fr/vue-router).* +# Champs meta de route -You can include a `meta` field when defining a route: +Vous pouvez inclure un champ `meta` quand vous définissez une route : ``` js const router = new VueRouter({ @@ -12,7 +12,7 @@ const router = new VueRouter({ { path: 'bar', component: Bar, - // a meta field + // un champ `meta` meta: { requiresAuth: true } } ] @@ -21,21 +21,21 @@ const router = new VueRouter({ }) ``` -So how do we access this `meta` field? +Comment maintenant accéder à ce champ `meta` ? -First, each route object in the `routes` configuration is called a **route record**. Route records may be nested. Therefore when a route is matched, it can potentially match more than one route record. +Tout d'abord, chaque objet route dans la configuration de `routes` est appelé un **registre de route**. Les registres de route peuvent être imbriqués. Par conséquent, quand une route concorde, elle peut potentiellement concorder avec plus d'un registre de route. -For example, with the above route config, the URL `/foo/bar` will match both the parent route record and the child route record. +Par exemple, avec la configuration de route ci-dessous, l'URL `/foo/bar` va concorder avec le registre parent et le registre enfant. -All route records matched by a route are exposed on the `$route` object (and also route objects in navigation guards) as the `$route.matched` Array. Therefore, we will need to iterate over `$route.matched` to check for meta fields in route records. +Tous les registres concordants avec une route sont exposés dans l'objet `$route` (ainsi que les objets de route dans les sécurisations de navigation) dans le tableau `$route.matched`. Donc, nous devons itérer à travers `$route.matched` pour vérifier les champs meta dans les registres de route. -An example use case is checking for a meta field in the global navigation guard: +Un exemple concret est la vérification d'un champ meta dans une interception de navigation globale : ``` js router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { - // this route requires auth, check if logged in - // if not, redirect to login page. + // cette route demande une autorisation, vérifions si l'utilisateur est logué. + // sinon, redirigeons le sur la page de login. if (!auth.loggedIn()) { next({ path: '/login', @@ -45,7 +45,7 @@ router.beforeEach((to, from, next) => { next() } } else { - next() // make sure to always call next()! + next() // assurez vous de toujours appeler `next()` ! } }) ``` diff --git a/docs/fr/advanced/navigation-guards.md b/docs/fr/advanced/navigation-guards.md index 97dfb6903..4937bb1ae 100644 --- a/docs/fr/advanced/navigation-guards.md +++ b/docs/fr/advanced/navigation-guards.md @@ -1,12 +1,12 @@ -# Navigation Guards (En)

    *Cette page est en cours de traduction française. Revenez une autre fois pour lire une traduction achevée ou [participez à la traduction française ici](https://github.com/vuejs-fr/vue-router).* +# Intercepteurs de navigation -As the name suggests, the navigation guards provided by `vue-router` are primarily used to guard navigations either by redirecting it or canceling it. There are a number of ways to hook into the route navigation process: globally, per-route, or in-component. +Comme le nom le suggère, l'interception de navigation fournie par `vue-router` est principalement utilisée pour intercepter la navigation avec des redirections ou des annulations d'accès. Il y a plusieurs hooks disponibles lors du processus de navigation : globaux, par route ou par composant. -Remember that **params or query changes won't trigger enter/leave navigation guards**. You can either [watch the `$route` object](../essentials/dynamic-matching.md#reacting-to-params-changes) to react to those changes, or use the `beforeRouteUpdate` in-component guard. +Souvenez-vous de cela : **le changement de paramètre ou de query ne va pas lancer d'interception d'entrée ou de sortie de navigation**. Vous pouvez toujours [observer l'objet `$route`](../essentials/dynamic-matching.md#reacting-to-params-changes) pour réagir à ces changements, ou utiliser la fonction `beforeRouteUpdate` d'une interception par composant. -### Global Guards +### Interception globale -You can register global before guards using `router.beforeEach`: +Vous pouvez abonner une interception d'entrée en utilisant `router.beforeEach` : ``` js const router = new VueRouter({ ... }) @@ -16,35 +16,35 @@ router.beforeEach((to, from, next) => { }) ``` -Global before guards are called in creation order, whenever a navigation is triggered. Guards may be resolved asynchronously, and the navigation is considered **pending** before all hooks have been resolved. +Les interceptions d'entrées globales sont appelées lors de l'ordre de création, chaque fois qu'une navigation est déclenchée. Les interceptions peuvent être résolues de manière asynchrone, et la navigation est considérée comme **en attente** avant que tous les hooks ne soient résolus. -Every guard function receives three arguments: +Chaque fonction d'interception reçoit trois arguments : -- **`to: Route`**: the target [Route Object](../api/route-object.md) being navigated to. +- **`to: Route`**: L'[objet `Route`](../api/route-object.md) cible vers lequel on navigue. -- **`from: Route`**: the current route being navigated away from. +- **`from: Route`**: la route courante depuis laquelle nous venons de naviguer. -- **`next: Function`**: this function must be called to **resolve** the hook. The action depends on the arguments provided to `next`: +- **`next: Function`**: cette fonction doit être appelée pour **résoudre** le hook. L'action dépend des arguments fournis à `next`: - - **`next()`**: move on to the next hook in the pipeline. If no hooks are left, the navigation is **confirmed**. + - **`next()`**: se déplacer jusqu'au prochain hook du workflow. S'il ne reste aucun hook, la navigation est **confirmée**. - - **`next(false)`**: abort the current navigation. If the browser URL was changed (either manually by the user or via back button), it will be reset to that of the `from` route. + - **`next(false)`**: annuler la navigation courante. Si l'URL du navigateur avait changé (manuellement par l'utilisateur ou via le bouton retour du navigateur), il sera remis à sa valeur de route de `from`. - - **`next('/')` or `next({ path: '/' })`**: redirect to a different location. The current navigation will be aborted and a new one will be started. + - **`next('/')` ou `next({ path: '/' })`**: redirige vers le nouvel URL. La navigation courante va être arrêtée et une nouvelle va se lancer. - - **`next(error)`**: (2.4.0+) if the argument passed to `next` is an instance of `Error`, the navigation will be aborted and the error will be passed to callbacks registered via `router.onError()`. + - **`next(error)`**: (2.4.0+) si l'argument passé à `next` est une instance de `Error`, la navigation va s'arrêter et l'erreur sera passée aux fonctions de rappel via `router.onError()`. -**Make sure to always call the `next` function, otherwise the hook will never be resolved.** +**Assurez-vous de toujours appeler la fonction `next`, sinon le hook ne sera jamais résolu.** -### Global Resolve Guards +### Résolutions des interceptions globales -> New in 2.5.0 +> Nouveau dans la 2.5.0 -In 2.5.0+ you can register a global guard with `router.beforeResolve`. This is similar to `router.beforeEach`, with the difference that resolve guards will be called right before the navigation is confirmed, **after all in-component guards and async route components are resolved**. +Dans la 2.5.0+ vous pouvez abonner une interception globale avec `router.beforeResolve`. Ceci est similaire a `router.beforeEach`, mais la différence est qu'elle sera appelée juste après que la navigation soit confirmée, **après que toutes les interceptions par composants et les composants de route asynchrone ait été résolu**. -### Global After Hooks +### Hooks de sortie globaux -You can also register global after hooks, however unlike guards, these hooks do not get a `next` function and cannot affect the navigation: +Vous pouvez également abonner des hooks de sortie, cependant, à la différence des interceptions, ces hooks ne fournissent pas de fonction `next` et n'affecte pas la navigation : ``` js router.afterEach((to, from) => { @@ -52,9 +52,9 @@ router.afterEach((to, from) => { }) ``` -### Per-Route Guard +### Interception par route -You can define `beforeEnter` guards directly on a route's configuration object: +Vous pouvez définir l'interception `beforeEnter` directement sur l'objet de configuration d'une route : ``` js const router = new VueRouter({ @@ -70,65 +70,65 @@ const router = new VueRouter({ }) ``` -These guards have the exact same signature as global before guards. +Ces interceptions ont exactement le même effet que les interceptions globales d'entrée. -### Sécurisation intra-composants +### Interception par composant -Finally, you can directly define route navigation guards inside route components (the ones passed to the router configuration) with the following options: +Enfin, vous pouvez directement définir une interception de navigation a l'intérieur du composant lui-même (celui passer à la configuration du routeur) avec les options suivantes : - `beforeRouteEnter` -- `beforeRouteUpdate` (added in 2.2) +- `beforeRouteUpdate` (ajouté dans la 2.2+) - `beforeRouteLeave` ``` js const Foo = { template: `...`, beforeRouteEnter (to, from, next) { - // called before the route that renders this component is confirmed. - // does NOT have access to `this` component instance, - // because it has not been created yet when this guard is called! + // appelée avant que la route vers le composant soit confirmée. + // cette fonction n'a pas accès à l'instance du composant avec `this`, + // car le composant n'a pas encore été créé quand cette interception est appelée ! }, beforeRouteUpdate (to, from, next) { - // called when the route that renders this component has changed, - // but this component is reused in the new route. - // For example, for a route with dynamic params /foo/:id, when we - // navigate between /foo/1 and /foo/2, the same Foo component instance - // will be reused, and this hook will be called when that happens. - // has access to `this` component instance. + // appelée quand la route qui fait le rendu de ce composant change, + // mais que ce composant est utilisé de nouveau dans la nouvelle route. + // Par exemple, pour une route avec le paramètre dynamique `/foo/:id`, quand nous + // naviguons entre `/foo/1` et `/foo/2`, la même instance du composant `Foo` + // va être réutilisée, et ce hook va être appelé quand cela arrivera. + // ce hook a accès à l'instance de ce composant via `this`. }, beforeRouteLeave (to, from, next) { - // called when the route that renders this component is about to - // be navigated away from. - // has access to `this` component instance. + // appelée quand la route qui fait le rendu de ce composant est sur le point + // d'être laissée en faveur de la prochaine route. + // elle a accès à l'instance de ce composant via `this`. } } ``` -The `beforeRouteEnter` guard does **NOT** have access to `this`, because the guard is called before the navigation is confirmed, thus the new entering component has not even been created yet. +L'interception `beforeRouteEnter` **n'**a **PAS** accès à `this`, car l'interception est appelée avant que la navigation soit confirmée, et le nouveau composant entrant n'a même pas encore été créé. -However, you can access the instance by passing a callback to `next`. The callback will be called when the navigation is confirmed, and the component instance will be passed to the callback as the argument: +Cependant, vous pouvez accéder à l'instance en passant dans la fonction de rappel `next`. Cette fonction de rappel va être appelée quand la navigation sera confirmée, et l'instance du composant sera passée à la fonction de rappel en tant qu'argument : ``` js beforeRouteEnter (to, from, next) { next(vm => { - // access to component instance via `vm` + // accès à l'instance du composant via `vm` }) } ``` -You can directly access `this` inside `beforeRouteLeave`. The leave guard is usually used to prevent the user from accidentally leaving the route with unsaved edits. The navigation can be canceled by calling `next(false)`. - -### The Full Navigation Resolution Flow - -1. Navigation triggered -2. Call leave guards in deactivated components -3. Call global `beforeEach` guards -4. Call `beforeRouteUpdate` guards in reused components (2.2+) -5. Call `beforeEnter` in route configs -6. Resolve async route components -7. Call `beforeRouteEnter` in activated components -8. Call global `beforeResolve` guards (2.5+) -9. Navigation confirmed. -10. Call global `afterEach` hooks. -11. DOM updates triggered. -12. Call callbacks passed to `next` in `beforeRouteEnter` guards with instantiated instances. +Vous pouvez directement accéder à `this` à l'intérieur de `beforeRouteLeave`. L'interception de sortie est utilisée pour empêcher l'utilisateur de quitter la route par accident alors qu'il n'a pas sauvé ses modifications. La navigation peut être annulée en appelant `next(false)`. + +### Le flux de résolution de navigation complet + +1. La navigation est demandée. +2. Appel de l'interception de sortie des composants désactivés (ceux que l'on va quitter). +3. Appel des interceptions globales `beforeEach`. +4. Appel des interceptions `beforeRouteUpdate` pour les composants réutilisés (2.2+). +5. Appel de `beforeEnter` dans la configuration de route. +6. Résolution des composants de route asynchrones. +7. Appel de `beforeRouteEnter` dans les composants activés (ceux où l'on va arriver). +8. Appel des interceptions `beforeResolve` (2.5+). +9. Confirmation de la navigation. +10. Appel des hooks globaux `afterEach`. +11. Modification du DOM demandée. +12. Appel des fonctions de rappel passées à `next` dans l'interception `beforeRouteEnter` avec l'instance instanciée. diff --git a/docs/fr/advanced/scroll-behavior.md b/docs/fr/advanced/scroll-behavior.md index 3215b18ff..5e14fd67a 100644 --- a/docs/fr/advanced/scroll-behavior.md +++ b/docs/fr/advanced/scroll-behavior.md @@ -34,7 +34,7 @@ scrollBehavior (to, from, savedPosition) { Cela permettra de défiler au haut de page à chaque navigation à travers les routes. -Retourner l'objet `savedPosition` résultera en un comportement quasi-natif en naviguant avec les boutons précédents/suivants : +Retourner l'objet `savedPosition` résultera en un comportement quasi natif en naviguant avec les boutons précédents/suivants : ``` js scrollBehavior (to, from, savedPosition) { diff --git a/docs/fr/advanced/transitions.md b/docs/fr/advanced/transitions.md index d90dc45cc..2bb63c47c 100644 --- a/docs/fr/advanced/transitions.md +++ b/docs/fr/advanced/transitions.md @@ -1,4 +1,4 @@ -# Les transitions +# Transitions Vu que `` est essentiellement un composant dynamique, on peut lui appliquer certains effets de transitions en utilisant le composant `` : @@ -8,11 +8,11 @@ Vu que `` est essentiellement un composant dynamique, on peut lui a ``` -[Tout à propos de ``](http://fr.vuejs.org/v2/guide/transitions.html) fonctionne également ici de la même manière. +[Tout à propos de ``](https://fr.vuejs.org/v2/guide/transitions.html) fonctionne également ici de la même manière. ### Transition par route -L'utilisation au dessus applique la même transition pour chaque route. Si vous voulez que les composants de route aient des transitions différentes, vous pouvez utiliser à la place `` avec des noms différents à l'intérieur de chaque composant de route : +L'utilisation du dessus applique la même transition pour chaque route. Si vous voulez que les composants de route aient des transitions différentes, vous pouvez utiliser à la place `` avec des noms différents à l'intérieur de chaque composant de route : ``` js const Foo = { diff --git a/docs/fr/api/component-injections.md b/docs/fr/api/component-injections.md index c98415a6b..c9f020a8b 100644 --- a/docs/fr/api/component-injections.md +++ b/docs/fr/api/component-injections.md @@ -18,4 +18,4 @@ Ces propriétés sont injectées dans chacun des composants enfants, en passant - **beforeRouteUpdate** (ajouté en 2.2) - **beforeRouteLeave** - Voir la [Sécurisation intra-composants](../advanced/navigation-guards.md#securisation-intra-composants). + Voir l'[interception par composant](../advanced/navigation-guards.md#securisation-par-composant). diff --git a/docs/fr/api/options.md b/docs/fr/api/options.md index 5ec49d1ce..f37e16530 100644 --- a/docs/fr/api/options.md +++ b/docs/fr/api/options.md @@ -65,7 +65,7 @@ - default : `"router-link-exact-active"` - Configure de manière globale la classe active par défaut de `` lors d'une correspondance exact. Voir aussi [router-link](router-link.md). + Configure de manière globale la classe active par défaut de `` lors d'une correspondance exacte. Voir aussi [router-link](router-link.md). ### scrollBehavior @@ -89,7 +89,7 @@ - type : `Function` - Permet de spécifier des fonctions personnalisées pour formater en objet ou en chaîne de caractères la requête. Surcharge les fonctions par défaut. + Permettent de spécifier des fonctions personnalisées pour formater en objet ou en chaîne de caractères la requête. Surcharge les fonctions par défaut. ### fallback @@ -99,4 +99,4 @@ Contrôle comment le routeur devrait passer en mode `hash` quand le navigateur ne supporte pas `history.pushState`. Par défaut à `true`. - Passer cette valeur à `false` va essentiellement faire que la navigation via `router-link` va réclamer un rechargement de page dans IE9. Ceci est utile quand l'application est rendu côté serveur et à besoin de fonctionner dans IE9, car le mode hash ne fonctionne pas avec du SSR. + Passer cette valeur à `false` va essentiellement faire que la navigation via `router-link` va réclamer un rechargement de page dans IE9. Ceci est utile quand l'application est rendue côté serveur et à besoin de fonctionner dans IE9, car le mode hash ne fonctionne pas avec du SSR. diff --git a/docs/fr/api/route-object.md b/docs/fr/api/route-object.md index fb9696c34..d10c1103e 100644 --- a/docs/fr/api/route-object.md +++ b/docs/fr/api/route-object.md @@ -1,6 +1,6 @@ # L'objet `Route` -Un **objet `Route`** représente l'état actuel de la route active. Il contient des informations analysées à propos de l'URL courante et **les itinéraires de route** appariés par l'URL. +Un **objet `Route`** représente l'état actuel de la route active. Il contient des informations analysées à propos de l'URL courant et **les itinéraires de route** appariés par l'URL. L'objet `Route` est immutable. Chaque navigation qui se déroule avec succès résultera en un nouvel objet `Route`. @@ -12,7 +12,7 @@ L'objet `Route` peut être trouvé à plusieurs endroits : - Comme valeur de retour après l'appel de `router.match(location)` -- À l'intérieur des fonctions de sécurisation de la navigation, dans les deux premiers paramètres de la fonction : +- À l'intérieur des fonctions d'interception de la navigation, dans les deux premiers paramètres de la fonction : ``` js router.beforeEach((to, from, next) => { @@ -36,7 +36,7 @@ L'objet `Route` peut être trouvé à plusieurs endroits : - type : `string` - Une chaîne de caractères représentant le chemin de la route en cours, toujours résolue en tant que chemin absolu, ex : `"/foo/bar"`. + Une chaine de caractères représentant le chemin de la route en cours, toujours résolue en tant que chemin absolu, ex : `"/foo/bar"`. - **$route.params** @@ -48,19 +48,19 @@ L'objet `Route` peut être trouvé à plusieurs endroits : - type : `Object` - Un objet qui contient des pairs clé/valeur de la requête au format d'une chaîne de caractères. Par exemple, pour un chemin `/foo?user=1`, on aura `$route.query.user == 1`. S'il n'y a pas de requête, alors la valeur sera un objet vide. + Un objet qui contient des pairs clé/valeur de la requête au format d'une chaine de caractères. Par exemple, pour un chemin `/foo?user=1`, on aura `$route.query.user == 1`. S'il n'y a pas de requête, alors la valeur sera un objet vide. - **$route.hash** - type : `string` - Le hash de la route courante (avec le `#`), s'il y en a un. S'il n'y a pas de hash, alors la valeur sera une chaîne de caractères vide. + Le hash de la route courante (avec le `#`), s'il y en a un. S'il n'y a pas de hash, alors la valeur sera une chaine de caractères vide. - **$route.fullPath** - type : `string` - L'URL entièrement résolue, incluant la requête et le hash. + L'URL entièrement résolu, incluant la requête et le hash. - **$route.matched** diff --git a/docs/fr/api/router-instance.md b/docs/fr/api/router-instance.md index 348301bd3..43498fc98 100644 --- a/docs/fr/api/router-instance.md +++ b/docs/fr/api/router-instance.md @@ -26,9 +26,9 @@ - **router.beforeResolve(guard)** (2.5.0+) - **router.afterEach(hook)** - Ajout de sécurités globales de navigation. Voir les [sécurités de navigation](../advanced/navigation-guards.md). + Ajout des interceptions globales de navigation. Voir les [Intercepteurs de navigation](../advanced/navigation-guards.md). - Dans la version 2.5.0+, ces trois méthodes retournent une fonction qui enlève les fonctions de sécurisations et hooks enregistrés. + Dans la version 2.5.0+, ces trois méthodes retournent une fonction qui enlève les fonctions d'interception et hooks enregistrés. - **router.push(location, onComplete?, onAbort?)** - **router.replace(location, onComplete?, onAbort?)** @@ -40,14 +40,14 @@ - **router.getMatchedComponents(location?)** - Retourne un tableau de composants (définition/constructeur et non les instances) correspondant à la `location` passée en paramètre, ou alors de la route actuelle. Cette fonction est principalement utilisée pendant le rendu côté serveur afin d'effectuer une pré-récupération des données. - + Retourne un tableau de composants (définition/constructeur et non les instances) correspondant à la `location` passée en paramètre, ou alors de la route actuelle. Cette fonction est principalement utilisée pendant le rendu côté serveur afin d'effectuer une prérécupération des données. + - **router.resolve(location, current?, append?)** > 2.1.0+ Inverse la résolution d'URL. La `location` doit avoir la même forme qu'utilisée dans ``, retourne un objet avec les propriétés suivantes : - + ``` js { location: Location; @@ -57,14 +57,14 @@ ``` - `current` is the current Route by default (most of the time you don't need to change this) - - `append` allows you to append the path to the `current` route (as with [`router-link`](https://router.vuejs.org/en/api/router-link.html#props)) + - `append` allows you to append the path to the `current` route (as with [`router-link`](router-link.md#props)) - **router.addRoutes(routes)** > 2.2.0+ Permet d'ajouter dynamiquement des routes au routeur. L'argument doit être un tableau utilisant le même format de configuration que l'option `routes` du constructeur. - + - **router.onReady(callback, [errorCallback])** > 2.2.0+ @@ -73,16 +73,16 @@ C'est utile pendant un rendu côté serveur pour assurer une sortie consistance sur le serveur et le client. - Le deuxième argument `errorCallback` est uniquement supporté à partir de la version 2.4. Il sera appelé lorsque la résolution de la route initiale résultera en une erreur (ex : la résolution d'un composant asynchrone qui a échouée). - + Le deuxième argument `errorCallback` est uniquement supporté à partir de la version 2.4. Il sera appelé lorsque la résolution de la route initiale résultera en une erreur (ex. : la résolution d'un composant asynchrone qui a échoué). + - **router.onError(callback)** > 2.4.0+ Enregistre une fonction de rappel qui sera appelée lorsqu'une erreur sera capturée pendant la navigation vers une route. Notez que pour qu'une erreur soit appelée, cela doit correspondre à l'un des scénarios suivants : - - L'erreur est lancée de manière synchrone à l'intérieur d'une fonction de sécurisation de route ; + - L'erreur est lancée de manière synchrone à l'intérieur d'une fonction d'interception de route ; + + - L'erreur est capturée et traitée de manière asynchrone en appelant `next(err)` à l'intérieur d'une fonction d'interception de route ; - - L'erreur est capturée et traitée de manière asynchrone en appelant `next(err)` à l'intérieur d'une fonction de sécurisation de route ; - - Une erreur est survenue pendant la résolution d'un composant asynchrone qui est requis pour faire le rendu d'une route. diff --git a/docs/fr/api/router-link.md b/docs/fr/api/router-link.md index 74fe81872..e1b7a47d5 100644 --- a/docs/fr/api/router-link.md +++ b/docs/fr/api/router-link.md @@ -1,14 +1,14 @@ # `` -`` est le composant pour activer la navigation utilisateur dans une application où le routeur est activé. La localisation cible est spécifiée grâce à la prop `to`. Il est rendu en tant que balise `` avec le `href` correct par défaut, mais peut être configuré grâce à la prop `tag`. De plus, le lien se vera attribuer une classe CSS active lorsque la route cible est active. +`` est le composant pour activer la navigation utilisateur dans une application où le routeur est activé. La localisation cible est spécifiée grâce à la prop `to`. Il est rendu en tant que balise `` avec le `href` correct par défaut, mais peut être configuré grâce à la prop `tag`. De plus, le lien se verra attribuer une classe CSS active lorsque la route cible est active. `` est préféré par rapport au `` en dur dans le code pour les raisons suivantes : - Cela fonctionne de la même manière qu'on soit dans le mode historique HTML5 ou le mode hash, donc si vous avez décidé de changer de mode, ou alors que le routeur se replie sur le mode hash pour IE9, rien n'a besoin d'être changé. -- Dans le mode historique HTML5, `router-link` interceptera l'évènement du clic, comme ça le navigateur n'essaiera pas de rafraîchir la page. +- Dans le mode historique HTML5, `router-link` interceptera l'évènement du clic, comme ça le navigateur n'essaiera pas de rafraichir la page. -- En utilisant l'option `base` dans le mode historique HTML5, vous n'avez pas besoin de l'inclure dans les props `to` des URLs. +- En utilisant l'option `base` dans le mode historique HTML5, vous n'avez pas besoin de l'inclure dans les props `to` des URL. ### Props @@ -18,10 +18,10 @@ - requis - Désigne la route cible du lien. Lorsqu'il est cliqué, la valeur de la prop `to` va être passée de manière interne à `router.push`, donc la valeur peut soit être une chaîne de caractères, ou alors un objet décrivant une localisation. - + Désigne la route cible du lien. Lorsqu'il est cliqué, la valeur de la prop `to` va être passée de manière interne à `router.push`, donc la valeur peut soit être une chaine de caractères, ou alors un objet décrivant une localisation. + ``` html - + Accueil Accueil @@ -29,10 +29,10 @@ Accueil - + Accueil - + Accueil @@ -63,7 +63,7 @@ - défaut : `false` Configurer la propriété `append` suffixe toujours le chemin relatif au chemin courant. Par exemple, assumons que nous naviguons de `/a` à un lien relatif `b`, sans `append` on finira sur `/b`, mais avec `append` on finira sur `/a/b`. - + ``` html ``` @@ -75,7 +75,7 @@ - défaut : `"a"` - Parfois, on veut que `` soit rendu avec une balise différente, ex : `
  • `. On peut alors utiliser la prop `tag` pour modifier la balise qui sera rendue, et elle écoutera toujours les évènements de clic pour la navigation + Parfois, on veut que `` soit rendu avec une balise différente, ex : `
  • `. On peut alors utiliser la prop `tag` pour modifier la balise qui sera rendue, et elle écoutera toujours les évènements de clic pour la navigation. ``` html foo @@ -98,7 +98,7 @@ - défaut : `false` - Le comportement par défaut de la correspondance de classe active est une **correspondance inclusive**. Par exemple, `` vera cette classe appliquée tant que le chemin courant commencera par `/a/` ou `/a`. + Le comportement par défaut de la correspondance de classe active est une **correspondance inclusive**. Par exemple, `` verra cette classe appliquée tant que le chemin courant commencera par `/a/` ou `/a`. Une conséquence de cela est que `` sera actif pour toutes les routes ! Pour forcer le lien dans un « mode correspondance exacte », utilisez la prop `exact`. @@ -106,7 +106,7 @@ ``` - + Allez voir les exemples expliquant la classe active pour les liens [ici](https://jsfiddle.net/8xrk1n9f/). - **event** @@ -127,7 +127,7 @@ - défaut : `"router-link-exact-active"` - Configure la classe CSS active qui sera appliquée lorsqu'un lien sera actif avec une correspondance exact. Notez que la valeur par défaut peut aussi être configurée de manière globale via l'option `linkExactActiveClass` du constructeur du routeur. + Configure la classe CSS active qui sera appliquée lorsqu'un lien sera actif avec une correspondance exacte. Notez que la valeur par défaut peut aussi être configurée de manière globale via l'option `linkExactActiveClass` du constructeur du routeur. ### Appliquer la classe active à l'élément extérieur @@ -139,4 +139,4 @@ Parfois, on voudrait que la classe active soit appliquée à un élément extér ``` -Dans ce cas, `` sera le lien actuel (et récupérera le bon `href`), mais la classe active sera appliquée à l'élément extérieur `
  • `. +Dans ce cas, `` sera le lien actuel (et récupèrera le bon `href`), mais la classe active sera appliquée à l'élément extérieur `
  • `. diff --git a/docs/fr/api/router-view.md b/docs/fr/api/router-view.md index abce22d1d..619a2f8db 100644 --- a/docs/fr/api/router-view.md +++ b/docs/fr/api/router-view.md @@ -10,7 +10,7 @@ Le composant `` est un composant fonctionnel qui fait le rendu du c - défaut : `"default"` - Lorsqu'un `` a un nom, il fera le rendu du composant correspondant à ce nom dans les itinéraires de route correspondant à l'option `components`. Voir les [Routes nommées](../essentials/named-views.md) pour un example. + Lorsqu'un `` a un nom, il fera le rendu du composant correspondant à ce nom dans les itinéraires de route correspondant à l'option `components`. Voir les [Routes nommées](../essentials/named-views.md) pour un exemple. ### Comportement diff --git a/docs/fr/essentials/dynamic-matching.md b/docs/fr/essentials/dynamic-matching.md index e764e8e49..9f84d99c5 100644 --- a/docs/fr/essentials/dynamic-matching.md +++ b/docs/fr/essentials/dynamic-matching.md @@ -15,7 +15,7 @@ const router = new VueRouter({ }) ``` -Maintenant des URLs comme `/utilisateur/foo` et `/utilisateur/bar` seront chacune associée à la même route. +Maintenant des URL comme `/utilisateur/foo` et `/utilisateur/bar` seront chacun associé à la même route. Un segment dynamique se repère avec les deux-points `:`. Quand une route concorde, la valeur du segment dynamique est exposée via `this.$route.params` dans tous les composants. Et donc, nous pouvons faire le rendu de l'identifiant de l'utilisateur courant en mettant à jour le template de `User` ainsi : @@ -25,7 +25,7 @@ const User = { } ``` -Vous pouvez regarder un exemple en ligne [ici](http://jsfiddle.net/yyx990803/4xfa2f19/). +Vous pouvez regarder un exemple en ligne [ici](https://jsfiddle.net/yyx990803/4xfa2f19/). Vous pouvez avoir plusieurs segments dynamiques pour une même route, et ils seront associés aux champs associés dans `$route.params`. Des exemples : @@ -53,7 +53,7 @@ const User = { } ``` -Ou utiliser la fonction de sécurisation `beforeRouteUpdate` introduite avec la 2.2 : +Ou utiliser la fonction d'interception `beforeRouteUpdate` introduite avec la 2.2 : ``` js const User = { @@ -67,8 +67,8 @@ const User = { ### Motifs de concordance avancés -`vue-router` utilise [path-to-regexp](https://github.com/pillarjs/path-to-regexp) comme moteur de concordance de chemin, il supporte donc plusieurs motifs de concordance avancés tel que la présence optionnelle de segments dynamiques, aucun ou plusieurs motifs, plus d'options par motifs, et même des motifs d'expressions régulières personnalisés. Consultez cette [documentation](https://github.com/pillarjs/path-to-regexp#parameters) pour utiliser ces motifs avancés et [cet exemple](https://github.com/vuejs/vue-router/blob/dev/examples/route-matching/app.js) pour les utiliser avec `vue-router`. +`vue-router` utilise [path-to-regexp](https://github.com/pillarjs/path-to-regexp) comme moteur de concordance de chemin, il supporte donc plusieurs motifs de concordance avancés tels que la présence optionnelle de segments dynamiques, aucun ou plusieurs motifs, plus d'options par motifs, et même des motifs d'expressions régulières personnalisés. Consultez cette [documentation](https://github.com/pillarjs/path-to-regexp#parameters) pour utiliser ces motifs avancés et [cet exemple](https://github.com/vuejs/vue-router/blob/dev/examples/route-matching/app.js) pour les utiliser avec `vue-router`. ### Priorité de concordance -Parfois la même URL peut être adressée par de multiples routes. Dans ce cas, la priorité de concordance est déterminée par l'ordre de la définition des routes : plus la route est définie tôt, plus sa priorité est élevée. +Parfois la même URL peut être adressé par de multiples routes. Dans ce cas, la priorité de concordance est déterminée par l'ordre de la définition des routes : plus la route est définie tôt, plus sa priorité est élevée. diff --git a/docs/fr/essentials/getting-started.md b/docs/fr/essentials/getting-started.md index 05e3cf2c7..4dee272f9 100644 --- a/docs/fr/essentials/getting-started.md +++ b/docs/fr/essentials/getting-started.md @@ -2,7 +2,7 @@ > Nous utiliserons [ES2015](https://github.com/lukehoban/es6features) dans les exemples de code dans ce guide. -Créer une application monopage avec Vue.js + vue-router est vraiment simple. Avec Vue.js, nous concevons déjà notre application avec des composants. En ajoutant vue-router dans notre application, tout ce qu'il nous reste à faire est de relier nos composants aux routes, et de laisser vue-router faire le rendu. Voici un exemple de base : +Créer une application monopage avec Vue + Vue Router est vraiment simple. Avec Vue.js, nous concevons déjà notre application avec des composants. En ajoutant vue-router dans notre application, tout ce qu'il nous reste à faire est de relier nos composants aux routes, et de laisser vue-router faire le rendu. Voici un exemple de base : > Tous les exemples utiliseront la version complète de Vue pour rendre l'analyse de template possible. Plus de détails [ici](https://fr.vuejs.org/guide/installation.html#Runtime-Compiler-vs-Runtime-seul). @@ -30,7 +30,7 @@ Créer une application monopage avec Vue.js + vue-router est vraiment simple. Av ### JavaScript ``` js -// 0. Si vous utilisez un système de module (ex : via vue-cli), il faut importer Vue et VueRouter et ensuite appeler `Vue.use(VueRouter)`. +// 0. Si vous utilisez un système de module (par ex. via vue-cli), il faut importer Vue et Vue Router et ensuite appeler `Vue.use(VueRouter)`. // 1. Définissez les composants de route. // Ces derniers peuvent être importés depuis d'autre fichier @@ -64,6 +64,6 @@ const app = new Vue({ // L'application est maintenant en marche ! ``` -Vous pouvez aussi regarder cet [exemple](http://jsfiddle.net/yyx990803/xgrjzsup/). +Vous pouvez aussi regarder cet [exemple](https://jsfiddle.net/yyx990803/xgrjzsup/). Notez qu'un `` obtiendra automatiquement la classe `.router-link-active` lorsque sa route cible correspond à la route actuelle. Vous pouvez en apprendre plus à propos de cela dans sa [documentation d'API](../api/router-link.md). diff --git a/docs/fr/essentials/history-mode.md b/docs/fr/essentials/history-mode.md index 04c74bdcb..d4eb4ad5d 100644 --- a/docs/fr/essentials/history-mode.md +++ b/docs/fr/essentials/history-mode.md @@ -1,8 +1,8 @@ -# HTML5 History Mode (En)

    *Cette page est en cours de traduction française. Revenez une autre fois pour lire une traduction achevée ou [participez à la traduction française ici](https://github.com/vuejs-fr/vue-router).* +# Mode historique de HTML5 -The default mode for `vue-router` is _hash mode_ - it uses the URL hash to simulate a full URL so that the page won't be reloaded when the URL changes. +Le mode par défaut de `vue-router` est le _mode hash_. Il utilise la partie hash de l'URL pour simuler un URL complet et ainsi ne pas recharger la page quand l'URL change. -To get rid of the hash, we can use the router's **history mode**, which leverages the `history.pushState` API to achieve URL navigation without a page reload: +Pour nous passer du hash, nous pouvons utiliser le **mode historique** qui utilisera l'API `history.pushState` afin de permettre une navigation sans rechargement de page : ``` js const router = new VueRouter({ @@ -11,13 +11,13 @@ const router = new VueRouter({ }) ``` -When using history mode, the URL will look "normal," e.g. `http://oursite.com/user/id`. Beautiful! +Quand vous utilisez le mode historique, l'URL ressemblera à n'importe quel URL normal. Par ex. `http://oursite.com/user/id`. Magnifique ! -Here comes a problem, though: Since our app is a single page client side app, without a proper server configuration, the users will get a 404 error if they access `http://oursite.com/user/id` directly in their browser. Now that's ugly. +Cependant, un problème apparait si votre application est une application monopage cliente. Sans une configuration serveur adaptée, les utilisateurs tomberont sur une page d'erreur 404 en tentant d'accéder à `http://oursite.com/user/id` directement dans leur navigateur. Maintenant ça craint. -Not to worry: To fix the issue, all you need to do is add a simple catch-all fallback route to your server. If the URL doesn't match any static assets, it should serve the same `index.html` page that your app lives in. Beautiful, again! +Ne vous inquiétez pas. Pour résoudre ce problème, il vous suffit d'ajouter une route à votre serveur prenant en compte toutes les adresses demandées. Si l'URL demandée ne concorde avec aucun fichier statique, alors il doit toujours renvoyer la page `index.html` qui contient le code de votre application. De nouveau magnifique ! -## Example Server Configurations +## Exemple de configurations serveur #### Apache @@ -40,12 +40,40 @@ location / { } ``` -#### Node.js (Express) +#### Node.js natif -For Node.js/Express, consider using [connect-history-api-fallback middleware](https://github.com/bripkens/connect-history-api-fallback). +```js +const http = require("http") +const fs = require("fs") +const httpPort = 80 -#### Internet Information Services (IIS) +http.createServer((req, res) => { + fs.readFile("index.htm", "utf-8", (err, content) => { + if (err) { + console.log(`Impossible d'ouvrir le fichier "index.htm"`) + } + + res.writeHead(200, { + "Content-Type": "text/html; charset=utf-8" + }) + + res.end(content) + }) +}).listen(httpPort, () => { + console.log("Le serveur écoute à : http://localhost:%s", httpPort) +}) ``` + +#### Node.js avec Express + +Pour Node.js avec Express, vous pouvez utiliser le [middleware connect-history-api-fallback](https://github.com/bripkens/connect-history-api-fallback). + +#### Internet Information Services (IIS) + +1. Instaler [IIS UrlRewrite](https://www.iis.net/downloads/microsoft/url-rewrite) +2. Créer un fichier `web.config` dans le répertoire racine de votre site avec le contenu suivant : + +```xml @@ -57,24 +85,26 @@ For Node.js/Express, consider using [connect-history-api-fallback middleware](ht - + - - - - - - - ``` -## Caveat +#### Caddy + +``` +rewrite { + regexp .* + to {path} / +} +``` + +## Limitation -There is a caveat to this: Your server will no longer report 404 errors as all not-found paths now serve up your `index.html` file. To get around the issue, you should implement a catch-all route within your Vue app to show a 404 page: +Il y a une limitation a tout ceci. Votre serveur ne renverra plus les erreurs 404 des chemins qui ne sont pas trouvés puisqu'il va servir à présent le fichier `index.html`. Pour contourner ce problème, vous pouvez implémenter une route concordant avec toutes les adresses en 404 dans votre application Vue : ``` js const router = new VueRouter({ @@ -85,4 +115,4 @@ const router = new VueRouter({ }) ``` -Alternatively, if you are using a Node.js server, you can implement the fallback by using the router on the server side to match the incoming URL and respond with 404 if no route is matched. +Une alternative possible, si vous utilisez un serveur Node.js, est d'implémenter ce mécanisme de substitution en utilisant le routeur côté serveur pour vérifier la concordance des demandes d'URL entrant. Si la route ne concorde avec rien, la page est inexistante. Consultez l'[utilisation de Vue côté serveur](https://ssr.vuejs.org/fr/) pour plus d'informations. diff --git a/docs/fr/essentials/named-routes.md b/docs/fr/essentials/named-routes.md index 6780b0297..0dc5e668e 100644 --- a/docs/fr/essentials/named-routes.md +++ b/docs/fr/essentials/named-routes.md @@ -1,6 +1,6 @@ # Routes nommées -Parfois il est plus pratique d'identifer une route avec un nom, tout particulièrement quand on souhaite attacher cette route ou exécuter des actions de navigation. Vous pouvez donner un nom à une route dans les options `routes` pendant la création de l'instance du routeur : +Parfois il est plus pratique d'identifier une route avec un nom, tout particulièrement quand on souhaite attacher cette route ou exécuter des actions de navigation. Vous pouvez donner un nom à une route dans les options `routes` pendant la création de l'instance du routeur : ``` js const router = new VueRouter({ diff --git a/docs/fr/essentials/named-views.md b/docs/fr/essentials/named-views.md index 844fe8515..ec25703f2 100644 --- a/docs/fr/essentials/named-views.md +++ b/docs/fr/essentials/named-views.md @@ -1,6 +1,6 @@ # Vues nommées -Parfois vous avez besoin d'afficher différentes vues en même temps plutôt que de les imbriquer, c.-à-d. créer un affichage avec une vue `sidebar` et une vue `main` par exemple. C'est ici que les routes nommées entre en jeu. Au lieu d'avoir une seule balise de vue, vous pouvez en avoir une multitude et donner à chacune d'entre elle un nom. Un `router-view` sans nom aura comme nom par défaut : `default`. +Parfois vous avez besoin d'afficher différentes vues en même temps plutôt que de les imbriquer, c.-à-d. créer un affichage avec une vue `sidebar` et une vue `main` par exemple. C'est ici que les routes nommées entrent en jeu. Au lieu d'avoir une seule balise de vue, vous pouvez en avoir une multitude et donner à chacune d'entre elles un nom. Un `router-view` sans nom aura comme nom par défaut : `default`. ``` html @@ -8,7 +8,7 @@ Parfois vous avez besoin d'afficher différentes vues en même temps plutôt que ``` -Une vue est rendue en utilisant un composant, donc de multiples vues nécessites de multiples composants pour une même route. Assurez-vous d'utiliser l'option `components` (avec un s) : +Une vue est rendue en utilisant un composant, donc de multiples vues nécessitent de multiples composants pour une même route. Assurez-vous d'utiliser l'option `components` (avec un s) : ``` js const router = new VueRouter({ @@ -25,4 +25,4 @@ const router = new VueRouter({ }) ``` -Une démo de cet exemple peut-être trouvée ici : [ici](https://jsfiddle.net/posva/6du90epg/). +Une démo de cet exemple peut-être trouvée [ici](https://jsfiddle.net/posva/6du90epg/). diff --git a/docs/fr/essentials/navigation.md b/docs/fr/essentials/navigation.md index 7008c9574..aa482f37e 100644 --- a/docs/fr/essentials/navigation.md +++ b/docs/fr/essentials/navigation.md @@ -6,7 +6,7 @@ En complément du l'utilisation de `` pour créer des balises ancre ** Note : Dans une instance Vue, vous pouvez accéder à l'instance du routeur via `$router`. Vous pouvez donc appeler `this.$router.push`.** -Pour naviguer vers une URL différente, utilisez `router.push`. Cette méthode ajoute une nouvelle entrée dans la pile de l'historique. Ainsi quand un utilisateur clique sur le bouton retour de son navigateur, il retournera à l'URL précédente. +Pour naviguer vers un URL différent, utilisez `router.push`. Cette méthode ajoute une nouvelle entrée dans la pile de l'historique. Ainsi quand un utilisateur clique sur le bouton retour de son navigateur, il retournera à l'URL précédent. Cette méthode est appelée en interne quand vous cliquez sur ``, donc cliquer sur `` est équivalent à appeler `router.push(...)`. @@ -14,10 +14,10 @@ Cette méthode est appelée en interne quand vous cliquez sur ``, d |-------------|--------------| | `` | `router.push(...)` | -L'argument peut être une chaîne de caractère représentant un chemin, ou un objet de description de destination. Des exemples : +L'argument peut être une chaine de caractère représentant un chemin, ou un objet de description de destination. Des exemples : ``` js -// chaîne de caractère représentant un chemin +// chaine de caractère représentant un chemin router.push('home') // objet @@ -30,8 +30,22 @@ router.push({ name: 'user', params: { userId: 123 }}) router.push({ path: 'register', query: { plan: 'private' }}) ``` +**Note :** `params` est ignoré si `path` est fourni, ce qui n'est pas le cas pour `query`, comme démontré dans l'exemple ci-dessous. À la place, vous devez fournir le `name` de la route ou manuellement spécifier le `path` complet avec tous les paramètres : + +```js +const userId = 123 +router.push({ name: 'user', params: { userId }}) // -> /user/123 +router.push({ path: `/user/${userId}` }) // -> /user/123 +// Ceci ne va PAS fonctionner +router.push({ path: '/user', params: { userId }}) // -> /user +``` + +Les mêmes règles s'appliquent pour la propriété `to` du composant `router-link`. + Dans la version 2.2.0+, vous pouvez optionnellement fournir les fonctions de rappel `onComplete` et `onAbort` à `router.push` ou `router.replace` en tant que deuxième et troisième arguments. Ces fonctions de rappel seront appelées quand la navigation sera respectivement ; complétée avec succès (après la résolution de tous les hooks asynchrones), ou arrêtée (navigation vers la même route ou vers une route différente avant que la navigation courante ne soit achevée). +**Note :** si la destination est la même que la route courante et que seuls les paramètres ont changés (par ex. naviguer d'un profil à l'autre `/utilisateurs/1` -> `/utilisateurs/2`), vous devrez utiliser [`beforeRouteUpdate`](./dynamic-matching.html#réactivité-aux-changements-de-paramètres) pour réagir aux changements (par ex. récupérer les informations de l'utilisateur). + #### `router.replace(location, onComplete?, onAbort?)` Il agit comme `router.push`. La seule différence est que la navigation se fait sans ajouter de nouvelle entrée dans la pile de l'historique. Comme son nom l'indique, il remplace l'entrée courante. @@ -43,7 +57,7 @@ Il agit comme `router.push`. La seule différence est que la navigation se fait #### `router.go(n)` -Cette méthode prend un seul nombre en tant que paramètre. Celui-ci indique de combien d'entrée vers l'avant ou vers l'arrière il faut naviguer dans la pile de l'historique, de la même manière qu'avec `window.history.go(n)`. +Cette méthode prend un seul nombre en tant que paramètre. Celui-ci indique de combien d'entrées vers l'avant ou vers l'arrière il faut naviguer dans la pile de l'historique, de la même manière qu'avec `window.history.go(n)`. Des exemples @@ -64,7 +78,7 @@ router.go(100) #### Manipulation de l'historique -Vous avez peut être remarqué que `router.push`, `router.replace` et `router.go` sont des équivalent de [`window.history.pushState`, `window.history.replaceState` et `window.history.go`](https://developer.mozilla.org/fr-FR/docs/Web/API/History), et qu'ils imitent les APIs de `window.history`. +Vous avez peut être remarqué que `router.push`, `router.replace` et `router.go` sont des équivalents de [`window.history.pushState`, `window.history.replaceState` et `window.history.go`](https://developer.mozilla.org/fr-FR/docs/Web/API/History), et qu'ils imitent les APIs de `window.history`. Donc, si vous utilisez déjà l'[API History des navigateurs](https://developer.mozilla.org/fr-FR/docs/Web/API/History_API), manipuler l'historique sera très simple avec vue-router. diff --git a/docs/fr/essentials/nested-routes.md b/docs/fr/essentials/nested-routes.md index 998dc05d1..be0fc9cfb 100644 --- a/docs/fr/essentials/nested-routes.md +++ b/docs/fr/essentials/nested-routes.md @@ -1,6 +1,6 @@ # Routes imbriquées -Les vrais interfaces utilisateurs d'application sont faites de composants imbriqués à de multiples niveaux de profondeur. Il est aussi très commun que les segments d'URLs correspondent à une certaine structure de composants imbriqués, par exemple : +Les vraies interfaces utilisateurs d'application sont faites de composants imbriqués à de multiples niveaux de profondeur. Il est aussi très commun que les segments d'URL correspondent à une certaine structure de composants imbriqués, par exemple : ``` /utilisateur/foo/profil /utilisateur/foo/billets @@ -73,11 +73,11 @@ const router = new VueRouter({ }) ``` -**Notez que les chemins imbriqués commençant par `/` vont être traités comme des chemins partant de la racine. Cela vous permet d'adresser des composants imbriqués sans avoir à utiliser une URL imbriquée.** +**Notez que les chemins imbriqués commençants par `/` vont être traités comme des chemins partant de la racine. Cela vous permet d'adresser des composants imbriqués sans avoir à utiliser un URL imbriqué.** Comme vous pouvez le voir, l'option `children` n'est qu'un autre tableau d'objet de configuration de route comme dans `routes`. Et donc, vous pouvez garder les vues imbriquées au plus près de vos besoins. -À ce niveau, avec la configuration ci-dessus, quand vous visitez `/utilisateur/foo`, rien ne sera rendu dans la partie `User`, car aucune sous route ne concorde. Peut-être voudriez vous afficher quelque chose ici. Dans ce cas, vous pouvez fournir une sous route vide : +À ce niveau, avec la configuration ci-dessus, quand vous visitez `/utilisateur/foo`, rien ne sera rendu dans la partie `User`, car aucune sous route ne concorde. Peut-être voudriez-vous afficher quelque chose ici. Dans ce cas, vous pouvez fournir une sous route vide : ``` js const router = new VueRouter({ @@ -96,4 +96,4 @@ const router = new VueRouter({ }) ``` -Une démo de fonctionnement de cet exemple peut-être trouvée [ici](http://jsfiddle.net/yyx990803/L7hscd8h/). +Une démo de fonctionnement de cet exemple peut-être trouvée [ici](https://jsfiddle.net/yyx990803/L7hscd8h/). diff --git a/docs/fr/essentials/passing-props.md b/docs/fr/essentials/passing-props.md index 6ed2711c3..e77d77308 100644 --- a/docs/fr/essentials/passing-props.md +++ b/docs/fr/essentials/passing-props.md @@ -1,36 +1,36 @@ -# Passing Props to Route Components (En)

    *Cette page est en cours de traduction française. Revenez une autre fois pour lire une traduction achevée ou [participez à la traduction française ici](https://github.com/vuejs-fr/vue-router).* +# Passage de props aux composants de route -Using `$route` in your component creates a tight coupling with the route which limits the flexibility of the component as it can only be used on certain urls. +Utiliser `$route` dans vos composants crée un couplage fort à la route qui va limiter la flexibilité du composant qui ne pourra être utilisé que par certains URL. -To decouple this component from the router use props: +Pour découpler un composant de son routeur, utilisez les props : -**❌ Coupled to $route** +**❌ Couplé avec `$route`** ``` js const User = { - template: '
    User {{ $route.params.id }}
    ' + template: '
    Utilisateur {{ $route.params.id }}
    ' } const router = new VueRouter({ routes: [ - { path: '/user/:id', component: User } + { path: '/utilisateur/:id', component: User } ] }) ``` -**👍 Decoupled with props** +**👍 Découplé avec les `props`** ``` js const User = { props: ['id'], - template: '
    User {{ id }}
    ' + template: '
    Utilisateur {{ id }}
    ' } const router = new VueRouter({ routes: [ - { path: '/user/:id', component: User, props: true } - - // for routes with named views, you have to define the props option for each named view: + { path: '/utilisateur/:id', component: User, props: true } + + // pour les routes avec vues nommées, vous devez définir l'option `props` pour chaque vue nommée : { - path: '/user/:id', + path: '/utilisateur/:id', components: { default: User, sidebar: Sidebar }, props: { default: true, sidebar: false } } @@ -38,16 +38,15 @@ const router = new VueRouter({ }) ``` -This allows you to use the component anywhere, which makes the component easier to reuse and test. +Cela vous permet d'utiliser le composant n'importe où, ce qui le rend plus facile à réutiliser et à tester. -### Boolean mode +### Mode booléen -When props is set to true, the route.params will be set as the component props. +Quand `props` est mis à `true`, le `route.params` est remplis en tant que props du composant. -### Object mode +### Mode objet -When props is an object, this will be set as the component props as-is. -Useful for when the props are static. +Quand `props` est un objet, cela alimente les props de celui-ci. Utile quand les props sont statiques. ``` js const router = new VueRouter({ @@ -57,10 +56,9 @@ const router = new VueRouter({ }) ``` -### Function mode +### Mode fonction -You can create a function that returns props. -This allows you to cast the parameter to another type, combine static values with route-based values, etc. +Vous pouvez créer une fonction qui va retourner les props. Cela vous permet de caster des paramètres dans un autre type, de combiner les valeurs statiques avec les valeurs des routes, etc. ``` js const router = new VueRouter({ @@ -70,10 +68,8 @@ const router = new VueRouter({ }) ``` -The url: `/search?q=vue` would pass `{query: "vue"}` as props to the SearchUser component. - -Try to keep the props function stateless, as it's only evaluated on route changes. -Use a wrapper component if you need state to define the props, that way vue can react to state changes. +L'URL `/search?q=vue` passerait `{query: 'vue'}` comme `props` au composant `SearchUser`. +Essayez de garder la fonction de `props` sans état, car il n'est évalué que sur les changements de route. Utilisez un composant englobant si vous avez besoin d'état pour définir les props, ainsi la vue pourra réagir au changement d'état. -For advanced usage, checkout the [example](https://github.com/vuejs/vue-router/blob/dev/examples/route-props/app.js). +Pour une utilisation avancée, jetez un œil à cet [exemple](https://github.com/vuejs/vue-router/blob/dev/examples/route-props/app.js). diff --git a/docs/fr/essentials/redirect-and-alias.md b/docs/fr/essentials/redirect-and-alias.md index bcd9954dd..0a791ee51 100644 --- a/docs/fr/essentials/redirect-and-alias.md +++ b/docs/fr/essentials/redirect-and-alias.md @@ -39,9 +39,9 @@ Pour d'autres utilisations avancées, jetez un œil à cet [exemple](https://git ### Alias -Une redirection signifie que si l'utilisateur visite `/a`, l'URL va être remplacée par `/b` et concordé avec `/b`. Mais qu'est-ce qu'un alias ? +Une redirection signifie que si l'utilisateur visite `/a`, l'URL va être remplacé par `/b` et concordé avec `/b`. Mais qu'est-ce qu'un alias ? -** Un alias de `/a` en tant que `/b` signifie que lorsque l'utilisateur va visiter `/b`, l'URL vas rester `/b`, mais la concordance va se faire comme si l'utilisateur visitait `/a`.** +** Un alias de `/a` en tant que `/b` signifie que lorsque l'utilisateur va visiter `/b`, l'URL va rester `/b`, mais la concordance va se faire comme si l'utilisateur visitait `/a`.** La phase du dessus peut être exprimée dans la configuration de la route de la manière suivante : @@ -53,6 +53,6 @@ const router = new VueRouter({ }) ``` -Un alias vous donne la liberté d'associer une structure d'interface utilisateur à une URL arbitraire, au lieu d'être contraint par une configuration de structure. +Un alias vous donne la liberté d'associer une structure d'interface utilisateur à un URL arbitraire, au lieu d'être contraint par une configuration de structure. Pour d'autres utilisations avancées, jetez un œil à cet [exemple](https://github.com/vuejs/vue-router/blob/dev/examples/route-alias/app.js). diff --git a/docs/fr/installation.md b/docs/fr/installation.md index b57f2cea6..21ead6cbe 100644 --- a/docs/fr/installation.md +++ b/docs/fr/installation.md @@ -5,7 +5,7 @@ [https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js) -[Unpkg.com](https://unpkg.com) fournit des liens CDN basés sur NPM. Le lien ci-dessus pointera toujours vers la dernière release sur NPM. Vous pouvez aussi utiliser un tag ou une version spécifique via une URL comme `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js`. +[Unpkg.com](https://unpkg.com) fournit des liens CDN basés sur npm. Le lien ci-dessus pointera toujours vers la dernière version sur npm. Vous pouvez aussi utiliser un tag ou une version spécifique via un URL comme `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js`. Incluez `vue-router` après Vue et l'installation sera automatique : @@ -15,7 +15,7 @@ Incluez `vue-router` après Vue et l'installation sera automatique : ``` -### NPM +### npm ``` bash npm install vue-router diff --git a/docs/gitbook/images/favicon.ico b/docs/gitbook/images/favicon.ico new file mode 100644 index 000000000..e481e5dda Binary files /dev/null and b/docs/gitbook/images/favicon.ico differ diff --git a/docs/ja/advanced/navigation-guards.md b/docs/ja/advanced/navigation-guards.md index e1cc570e3..623c3cc16 100644 --- a/docs/ja/advanced/navigation-guards.md +++ b/docs/ja/advanced/navigation-guards.md @@ -91,8 +91,8 @@ const Foo = { beforeRouteUpdate (to, from, next) { // このコンポーネントを描画するルートが変更されたときに呼び出されますが、 // このコンポーネントは新しいルートで再利用されます。 - // たとえば、動的な引数 /foo/:id を持つルートの場合、/foo/1 と /foo/2 の間を移動すると、 - // 同じ Foo コンポーネントインスタンスが再利用され、そのときにこのフックが呼び出されます。 + // たとえば、動的な引数 `/foo/:id` を持つルートの場合、`/foo/1` と `/foo/2` の間を移動すると、 + // 同じ `Foo` コンポーネントインスタンスが再利用され、そのときにこのフックが呼び出されます。 // `this` でコンポーネントインスタンスにアクセスできます。 }, beforeRouteLeave (to, from, next) { diff --git a/docs/ja/api/options.md b/docs/ja/api/options.md index 5f3e0941b..3a635245c 100644 --- a/docs/ja/api/options.md +++ b/docs/ja/api/options.md @@ -13,6 +13,7 @@ name?: string; // 名前付きルート用 components?: { [name: string]: Component }; // 名前付き view 用 redirect?: string | Location | Function; + props?: boolean | string | Function; alias?: string | Array; children?: Array; // ネストされたルート用 beforeEnter?: (to: Route, from: Route, next: Function) => void; diff --git a/docs/ja/essentials/history-mode.md b/docs/ja/essentials/history-mode.md index 106d83c1d..b3b7cfe41 100644 --- a/docs/ja/essentials/history-mode.md +++ b/docs/ja/essentials/history-mode.md @@ -40,12 +40,40 @@ location / { } ``` +#### Native Node.js + +```js +const http = require("http") +const fs = require("fs") +const httpPort = 80 + +http.createServer((req, res) => { + fs.readFile("index.htm", "utf-8", (err, content) => { + if (err) { + console.log('We cannot open "index.htm" file.') + } + + res.writeHead(200, { + "Content-Type": "text/html; charset=utf-8" + }) + + res.end(content) + }) +}).listen(httpPort, () => { + console.log("Server listening on: http://localhost:%s", httpPort) +}) +``` + #### Node.js (Express) Node.js/Express では [connect-history-api-fallback middleware](https://github.com/bripkens/connect-history-api-fallback) の利用を検討してください。 #### Internet Information Services (IIS) -``` + +1. [IIS UrlRewrite](https://www.iis.net/downloads/microsoft/url-rewrite) をインストール +2. 以下によるサイトのルートディレクトリに `web.config` ファイルを作成 + +``` xml @@ -57,21 +85,23 @@ Node.js/Express では [connect-history-api-fallback middleware](https://github. - + - - - - - - - ``` +#### Caddy + +``` +rewrite { + regexp .* + to {path} / +} +``` + ## 注意 この点に関して注意があります。全ての not-found パスが `index.html` を提供するため、もはや 404 エラーをサーバーがレポートしなくなります。回避策として、Vue アプリケーション内で 404 ページを表示するために catch-all ルートを実装すべきです。 @@ -85,4 +115,4 @@ const router = new VueRouter({ }) ``` -他の方法として、もしあなたが Node.js サーバーを使っている場合、入ってきた URL とマッチさせて、マッチしなかった場合に 404 を返答するサーバーサイドのルーターを使って fallback を実装することもできます。 +他の方法として、もしあなたが Node.js サーバーを使っている場合、入ってきた URL とマッチさせて、マッチしなかった場合に 404 を返答するサーバーサイドのルーターを使って fallback を実装することもできます。詳細については [Vue サーバサイドレンダリングのドキュメント](https://ssr.vuejs.org/ja/) を参照してください。 diff --git a/docs/ja/essentials/navigation.md b/docs/ja/essentials/navigation.md index 7c69d94b2..e1c9a69a5 100644 --- a/docs/ja/essentials/navigation.md +++ b/docs/ja/essentials/navigation.md @@ -31,8 +31,23 @@ router.push({ name: 'user', params: { userId: 123 }}) router.push({ path: 'register', query: { plan: 'private' }}) ``` +**注意**: `params` は、上記例に示すように、`path` が提供されている場合は無視されます。これは `query` に対するケースとは異なります。 +代わりに、ルートの `name` か任意のパラメータを付与した `path` 全体を手動で指定する必要があります: + +```js +const userId = 123 +router.push({ name: 'user', params: { userId }}) // -> /user/123 +router.push({ path: `/user/${userId}` }) // -> /user/123 +// これは動作"しません" +router.push({ path: '/user', params: { userId }}) // -> /user +``` + +同じルールが、`router-link` コンポーネントの `to` プロパティに対して適用されます。 + 2.2.0 以降では、必要に応じて、第 2 引数と第 3 引数として `router.push` または `router.replace` に `onComplete` と `onAbort` コールバックを指定します。これらのコールバックは、ナビゲーションが正常に完了したとき(すべての非同期フックが解決された後)に呼び出されるか、またはそれぞれ中止されます(現在のナビゲーションが終了する前に同じルートまたは別のルートにナビゲートされた) +**注意:** ルートの行き先が現在のルートと同じで、かつパラメータのみが変更されている場合(例: `/users/1` -> `/users/2` のようにあるプロファイルから他へ)、変更(例: ユーザー情報の取得など)に反応するために[beforeRouteUpdate](./dynamic-matching.html#パラメーター変更の検知) を使用しなければなりません。 + #### `router.replace(location, onComplete?, onAbort?)` これは `router.push` のように動作しますが、異なる点は新しい history エントリを追加しないで遷移することです。この名前から推定されるように、現在のエントリを置換します。 diff --git a/docs/ja/essentials/passing-props.md b/docs/ja/essentials/passing-props.md index 6c58f1846..338280220 100644 --- a/docs/ja/essentials/passing-props.md +++ b/docs/ja/essentials/passing-props.md @@ -1,10 +1,10 @@ # ルートコンポーネントにプロパティを渡す -コンポーネントで `$route` を使うと特定のURLでしか使用できないコンポーネントの柔軟性が制限されるルートで密結合を作り出します。 +コンポーネントで `$route` を使うとコンポーネントとルートの間に密結合が生まれ、コンポーネントが特定のURLでしか使用できないなど柔軟性が制限されます。 -このコンポーネントをルーターが使用するプロパティからこのコンポーネントを分離するには: +コンポーネントをルーターから分離するために `props` オプションを使います: -**❌ $route に結合** +**❌ `$route` に結合** ``` js const User = { @@ -17,7 +17,7 @@ const router = new VueRouter({ }) ``` -**👍 プロパティによる分離** +**👍 `props` による分離** ``` js const User = { @@ -28,9 +28,9 @@ const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, props: true } - // 名前付きビューによるルートに対しては、名前付きビューごとに props オプションを定義しなければなりません: + // 名前付きビューによるルートに対しては、名前付きビューごとに `props` オプションを定義しなければなりません: { - path: '/user/:id', + path: '/user/:id', components: { default: User, sidebar: Sidebar }, props: { default: true, sidebar: false } } @@ -42,12 +42,11 @@ const router = new VueRouter({ ### Boolean モード -props を true に設定すると、route.params がコンポーネントのプロパティとして設定されます。 +`props` を `true` に設定すると、`route.params` がコンポーネントのプロパティとして設定されます。 ### Object モード -props がオブジェクトの場合、これはコンポーネントプロパティとしてそのまま設定されます。 -プロパティが静的なときに便利です。 +`props` がオブジェクトの場合、これはコンポーネントプロパティとしてそのまま設定されます。プロパティが静的なときに便利です。 ``` js const router = new VueRouter({ @@ -59,8 +58,7 @@ const router = new VueRouter({ ### Function モード -プロパティを返す関数を作成することができます。 -これにより、パラメータを別のタイプにキャストし、静的な値をルートベースの値などと組み合わせることができます。 +プロパティを返す関数を作成することができます。これにより、パラメータを他のタイプにキャストし、静的な値をルートベースの値などと組み合わせることができます。 ``` js const router = new VueRouter({ @@ -70,8 +68,8 @@ const router = new VueRouter({ }) ``` -url `/search?q=vue` は `{query: "vue"}` をプロパティとして SearchUser コンポーネントに渡します。 +URL `/search?q=vue` は `{query: 'vue'}` をプロパティとして `SearchUser` コンポーネントに渡します。 -ルート変更時にのみ評価されるため、props 関数はステートレスにしてください。プロパティを定義するために状態を必要とする場合はラッパーコンポーネントを使用してください。その方法で vue は状態変更に対応することができます。 +ルート変更時にのみ評価されるため、`props` 関数はステートレスにしてください。プロパティを定義するために状態を必要とする場合はラッパーコンポーネントを使用してください。その方法で vue は状態変更に対応することができます。 高度な使い方については、[example](https://github.com/vuejs/vue-router/blob/dev/examples/route-props/app.js)を参照してください。 diff --git a/docs/ja/installation.md b/docs/ja/installation.md index d838ba1ff..2db7585bd 100644 --- a/docs/ja/installation.md +++ b/docs/ja/installation.md @@ -5,7 +5,7 @@ [https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js) -[Unpkg.com](https://unpkg.com) は NPM ベースの CDN リンクです。 上記のリンクは常に NPM 上の最新のリリースを指します。 `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js` のような URL を利用することで特定のバージョンやタグを指定することもできます。 +[Unpkg.com](https://unpkg.com) は npm ベースの CDN リンクです。 上記のリンクは常に NPM 上の最新のリリースを指します。 `https://unpkg.com/vue-router@2.0.0/dist/vue-router.js` のような URL を利用することで特定のバージョンやタグを指定することもできます。 Vue の後に `vue-router` を含めると自動的にインストールされます。 @@ -15,7 +15,7 @@ Vue の後に `vue-router` を含めると自動的にインストールされ ``` -### NPM +### npm ``` bash npm install vue-router diff --git a/docs/kr/SUMMARY.md b/docs/kr/SUMMARY.md index 117c75f77..d0d487377 100644 --- a/docs/kr/SUMMARY.md +++ b/docs/kr/SUMMARY.md @@ -23,9 +23,19 @@ - [스크롤 동작](advanced/scroll-behavior.md) - [지연된 로딩](advanced/lazy-loading.md) - API 레퍼런스 - - [router-link](api/router-link.md) - - [router-view](api/router-view.md) -  - [라우트 객체](api/route-object.md) - [라우터 생성자 옵션](api/options.md) + - [routes](api/options.md#routes) + - [mode](api/options.md#mode) + - [base](api/options.md#base) + - [linkActiveClass](api/options.md#linkactiveclass) + - [linkExactActiveClass](api/options.md#linkexactactiveclass) + - [scrollBehavior](api/options.md#scrollbehavior) + - [parseQuery / stringifyQuery](api/options.md#parsequery--stringifyquery) + - [fallback](api/options.md#fallback) - [라우터 인스턴스](api/router-instance.md) + - [Properties](api/router-instance.md#properties) + - [Methods](api/router-instance.md#methods) + - [라우트 객체](api/route-object.md) - [컴포넌트 주입](api/component-injections.md) + - [router-link](api/router-link.md) + - [router-view](api/router-view.md) diff --git a/docs/kr/advanced/data-fetching.md b/docs/kr/advanced/data-fetching.md index 03b5ec33e..e58ab8bd1 100644 --- a/docs/kr/advanced/data-fetching.md +++ b/docs/kr/advanced/data-fetching.md @@ -2,13 +2,13 @@ 때로는 라우트가 활성화될 때 서버에서 데이터를 가져와야 합니다. 예를 들어, 사용자 프로필을 렌더링하기 전에 서버에서 사용자의 데이터를 가져와야 합니다. 우리는 두 가지 방법을 사용할 수 있습니다. -- **탐색 후 가져 오기**: 먼저 탐색하고 들어오는 컴포넌트의 라이프 사이클 훅에서 데이터를 가져옵니다. 데이터를 가져 오는 동안 로드 상태를 표시합니다. +- **탐색 후 가져오기**: 먼저 탐색하고 들어오는 컴포넌트의 라이프 사이클 훅에서 데이터를 가져옵니다. 데이터를 가져오는 동안 로드 상태를 표시합니다. -- **탐색하기 전에 가져 오기**: 라우트 가드에서 경로를 탐색하기 전에 데이터를 가져오고 그 후에 탐색을 수행합니다. +- **탐색하기 전에 가져오기**: 라우트 가드에서 경로를 탐색하기 전에 데이터를 가져오고 그 후에 탐색을 수행합니다. 엄밀히 말하면 두 가지 모두 유효한 선택입니다. 궁극적인 목표는 사용자 경험에 달려 있습니다. -## 탐색 후 가져 오기 +## 탐색 후 가져오기 이 방법을 사용하면 들어오는 컴포넌트를 즉시 탐색하고 렌더링하며 컴포넌트의 `created` 훅에서 데이터를 가져옵니다. 네트워크를 통해 데이터를 가져 오는 동안 로드 상태를 표시 할 수 있는 기회를 제공하며 각 뷰 마다 로드를 다르게 처리 할 수도 있습니다. @@ -54,7 +54,7 @@ export default { fetchData () { this.error = this.post = null this.loading = true - // getPost를 데이터 가져 오기 위한 유틸리티/API 래퍼로 변경합니다. + // `getPost`를 데이터 가져오기 위한 유틸리티/API 래퍼로 변경합니다. getPost(this.$route.params.id, (err, post) => { this.loading = false if (err) { @@ -68,7 +68,7 @@ export default { } ``` -## 탐색하기 전에 가져 오기 +## 탐색하기 전에 가져오기 이 접근 방식을 사용하면 실제로 새 경로로 이동하기 전에 데이터를 가져옵니다. 들어오는 컴포넌트에서 `beforeRouteEnter` 가드에서 데이터를 가져올 수 있으며 페치가 완료되면 `next`만 호출 할 수 있습니다. @@ -95,7 +95,7 @@ export default { fetchData () { this.error = this.post = null this.loading = true - // getPost를 데이터 페치 유틸리티/API 래퍼로 바꿉니다. + // `getPost`를 데이터 페치 유틸리티/API 래퍼로 바꿉니다. getPost(this.$route.params.id, (err, post) => { this.loading = false if (err) { @@ -109,4 +109,4 @@ export default { } ``` -다음 뷰에 대한 리소스를 가져 오는 동안 사용자는 현재 뷰를 유지합니다. 따라서 데이터를 가져 오는 동안 진행률 표시줄이나 일종의 표시기를 표시하는 것을 추천합니다. 데이터 가져 오기가 실패하면 일종의 전역 경고 메시지를 표시해야합니다. \ No newline at end of file +다음 뷰에 대한 리소스를 가져 오는 동안 사용자는 현재 뷰를 유지합니다. 따라서 데이터를 가져 오는 동안 진행률 표시줄이나 일종의 표시기를 표시하는 것을 추천합니다. 데이터 가져오기가 실패하면 일종의 전역 경고 메시지를 표시해야합니다. diff --git a/docs/kr/advanced/lazy-loading.md b/docs/kr/advanced/lazy-loading.md index e8b7825b9..dba772649 100644 --- a/docs/kr/advanced/lazy-loading.md +++ b/docs/kr/advanced/lazy-loading.md @@ -2,26 +2,29 @@ 번들러를 이용하여 앱을 제작할 때 JavaScript 번들이 상당히 커져 페이지로드 시간에 영향을 줄 수 있습니다. 각 라우트의 컴포넌트를 별도의 단위로 분할하고 경로를 방문할 때 로드하는 것이 효율적일 것입니다. -Vue의 [비동기 컴포넌트](http://vuejs.org/guide/components.html#Async-Components)와 Webpack의 [코드 분할](https://webpack.js.org/guides/code-splitting-require/)을 결합합니다. 라우트 컴포넌트를 쉽게 불러올 수 있습니다. +Vue의 [비동기 컴포넌트](http://vuejs.org/guide/components.html#Async-Components)와 Webpack의 [코드 분할](https://webpack.js.org/guides/code-splitting-async/)을 결합합니다. 라우트 컴포넌트를 쉽게 불러올 수 있습니다. -라우트 컴포넌트를 비동기 컴포넌트로 정의하면됩니다. +첫째, 비동기 컴포넌트는 Promise를 반환하는 팩토리 함수로 정의할 수 있습니다 (컴포넌트가 resolve 되어야함). ``` js -const Foo = resolve => { - // require.ensure는 Webpack의 코드 분할에 대한 특수 구문입니다. - require.ensure(['./Foo.vue'], () => { - resolve(require('./Foo.vue')) - }) -} +const Foo = () => Promise.resolve({ /* 컴포넌트 정의 */ }) ``` -AMD 스타일 요구 사항을 사용하는 또다른 코드 분할 구문도 있으므로 다음과 같이 단순화 할 수 있습니다. +둘째, Webpack 2에서 [dynamic import](https://github.com/tc39/proposal-dynamic-import)를 사용하여 코드 분할 포인트를 지정할 수 있습니다. ``` js -const Foo = resolve => require(['./Foo.vue'], resolve) +import('./Foo.vue') // returns a Promise + ``` + + > 참고: Babel을 사용하고 있는 경우 올바른 구문 분석을 위해 [syntax-dynamic-import](http://babeljs.io/docs/plugins/syntax-dynamic-import/) 플러그인을 추가해야합니다. + + 이 두 가지를 결합하여 Webpack에 의해 자동으로 코드 분할될 비동기 컴포넌트를 정의하는 방법입니다. + + ``` js + const Foo = () => import('./Foo.vue') ``` -라우트 설정에서 아무것도 바꿀 필요가 없습니다. 보통 `Foo`만 사용하십시오. +라우트 설정에서 아무것도 바꿀 필요가 없습니다. `Foo`만 사용하면 됩니다. ``` js const router = new VueRouter({ @@ -33,12 +36,12 @@ const router = new VueRouter({ ### 같은 묶음으로 컴포넌트 그룹화하기 -때로는 동일한 라우트 아래에 중첩된 모든 컴포넌트를 동일한 비동기 묶음으로 그룹화 할 수 있습니다. 이를 위해 우리는 세 번째 전달인자로 `require.ensure`에 묶음 이름을 제공하여 [이름을 가진 묶음](https://webpack.js.org/guides/code-splitting-require/#chunkname)을 사용해야합니다. +때로는 동일한 라우트 아래에 중첩된 모든 컴포넌트를 동일한 비동기 묶음으로 그룹화 할 수 있습니다. 이를 위해 특수 주석 문법을 사용하는 이름(Webpack 2.4 이상 지원)을 제공하여 [이름을 가진 묶음](https://webpack.js.org/guides/code-splitting-async/#chunk-names)을 사용해야합니다. ``` js -const Foo = r => require.ensure([], () => r(require('./Foo.vue')), 'group-foo') -const Bar = r => require.ensure([], () => r(require('./Bar.vue')), 'group-foo') -const Baz = r => require.ensure([], () => r(require('./Baz.vue')), 'group-foo') +const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') +const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') +const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue') ``` -Webpack은 동일한 묶음 이름을 가진 비동기 모듈을 같은 비동기 묶음으로 그룹화합니다. 이것은 또한`require.ensure`에 대한 종속성을 더 이상 명시 적으로 나열할 필요가 없음을 의미합니다(따라서 빈 배열을 전달합니다). +Webpack은 동일한 이름의 묶음을 가진 비동기 모듈을 동일한 비동기 묶음으로 그룹화합니다. diff --git a/docs/kr/advanced/navigation-guards.md b/docs/kr/advanced/navigation-guards.md index 8f0153982..992d43785 100644 --- a/docs/kr/advanced/navigation-guards.md +++ b/docs/kr/advanced/navigation-guards.md @@ -41,7 +41,7 @@ router.beforeEach((to, from, next) => { > 2.5.0에서 추가됨 -2.5.0 이후로 `router.onResolve`를 사용하여 글로벌 가드를 등록 할 수 있습니다. 이는 `router.beforeEach`와 유사합니다. 모든 컴포넌트 가드와 비동기 라우트 컴포넌트를 불러온 후 네비게이션 가드를 확인하기 전에 호출된다는 차이가 있습니다 +2.5.0 이후로 `router.beforeResolve`를 사용하여 글로벌 가드를 등록 할 수 있습니다. 이는 `router.beforeEach`와 유사합니다. 모든 컴포넌트 가드와 비동기 라우트 컴포넌트를 불러온 후 네비게이션 가드를 확인하기 전에 호출된다는 차이가 있습니다 ### Global After Hooks @@ -110,17 +110,17 @@ beforeRouteEnter (to, from, next) { `beforeRouteLeave` 안에서 `this`에 직접 접근 할 수 있습니다. leave 가드는 일반적으로 사용자가 저장하지 않은 편집 내용을 두고 실수로 라우트를 떠나는 것을 방지하는데 사용됩니다. 탐색은 `next(false)`를 호출하여 취소할 수 있습니다. -### 전체 가드 시나리오 +### 전체 네비게이션 시나리오 -1. 네비게이션이 트리거됨 -2. 비활성화될 컴포넌트에서 가드를 호출 -3. 전역 `beforeEach` 가드 호출 -4. 재사용되는 컴포넌트에서 `beforeRouteUpdate` 가드 호출 (2.2 이상) -5. 라우트 설정에서 `beforeEnter` 호출 -6. 비동기 라우트 컴포넌트 해결 -7. 활성화된 컴포넌트에서 `beforeRouteEnter` 호출 -8. 전역 `beforeResolve` 가드 호출 (2.5이상) +1. 네비게이션이 트리거됨. +2. 비활성화될 컴포넌트에서 가드를 호출. +3. 전역 `beforeEach` 가드 호출. +4. 재사용되는 컴포넌트에서 `beforeRouteUpdate` 가드 호출. (2.2 이상) +5. 라우트 설정에서 `beforeEnter` 호출. +6. 비동기 라우트 컴포넌트 해결. +7. 활성화된 컴포넌트에서 `beforeRouteEnter` 호출. +8. 전역 `beforeResolve` 가드 호출. (2.5이상) 9. 네비게이션 완료. -10. 전역 `afterEach` 훅 호출 -11. DOM 갱신 트리거 됨 -12. 인스턴스화 된 인스턴스들의 `beforeRouteEnter`가드에서 `next`에 전달 된 콜백을 호출합니다 +10. 전역 `afterEach` 훅 호출. +11. DOM 갱신 트리거 됨. +12. 인스턴스화 된 인스턴스들의 `beforeRouteEnter`가드에서 `next`에 전달 된 콜백을 호출합니다. diff --git a/docs/kr/advanced/scroll-behavior.md b/docs/kr/advanced/scroll-behavior.md index 6325eb4a2..d95420673 100644 --- a/docs/kr/advanced/scroll-behavior.md +++ b/docs/kr/advanced/scroll-behavior.md @@ -20,7 +20,7 @@ const router = new VueRouter({ 이 함수는 스크롤 위치 객체를 반환 할 수 있습니다. 객체는 다음과 같은 형태 일 수 있습니다. - `{ x: number, y: number }` -- `{ selector: string }` +- `{ selector: string, offset? : { x: number, y: number }}` (offset은 2.6.0 이상만 지원) 잘못된 값이나 빈 객체가 반환되면 스크롤이 발생하지 않습니다. @@ -53,6 +53,7 @@ scrollBehavior (to, from, savedPosition) { if (to.hash) { return { selector: to.hash + // , offset: { x: 0, y: 10 } } } } diff --git a/docs/kr/advanced/transitions.md b/docs/kr/advanced/transitions.md index 042ed7cb0..c09e46fcf 100644 --- a/docs/kr/advanced/transitions.md +++ b/docs/kr/advanced/transitions.md @@ -44,7 +44,7 @@ const Bar = { ``` ``` js -// 그런 다음 부모 구성 요소에서 $route를 보고 사용할 전환을 결정합니다 +// 그런 다음 부모 구성 요소에서 `$route`를 보고 사용할 전환을 결정합니다 watch: { '$route' (to, from) { const toDepth = to.path.split('/').length diff --git a/docs/kr/api/options.md b/docs/kr/api/options.md index 2509ef9ca..88ae08607 100644 --- a/docs/kr/api/options.md +++ b/docs/kr/api/options.md @@ -18,6 +18,10 @@ beforeEnter?: (to: Route, from: Route, next: Function) => void; meta?: any; } + + // 2.6.0+ + caseSensitive?: boolean; // 대소문자를 구분합니까? (default: false) + pathToRegexpOptions?: Object; // path-to-regexp 옵션은 정규 표현식을 컴파일합니다. ``` ### mode @@ -84,4 +88,14 @@ - 자료형: `Function` - 사용자 지정 쿼리 문자열 구문 분석/문자열화 함수를 사용할 수 있습니다. 기본 값을 오버라이드합니다. \ No newline at end of file + 사용자 지정 쿼리 문자열 구문 분석/문자열화 함수를 사용할 수 있습니다. 기본 값을 오버라이드합니다. + +### fallback + + > 2.6.0+ + + - 자료형: `boolean` + + 브라우저가 `history.pushState`를 지원하지 않을 때 라우터가 `hash`모드로 변경되어야 할지 설정합니다. 기본값은 `true`입니다. + + 이를 `false`로 설정하면 IE9에서 모든 `router-link`를 탐색 할 수 있습니다. 이것은 해시모드 URL이 SSR에서 작동하지 않기 때문에 앱이 서버에서 렌더링되어 IE9에서 작동해야하는 경우에 유용합니다. diff --git a/docs/kr/api/router-instance.md b/docs/kr/api/router-instance.md index 352b1b406..3a4f9dbb3 100644 --- a/docs/kr/api/router-instance.md +++ b/docs/kr/api/router-instance.md @@ -56,6 +56,10 @@ } ``` +- `current` 현재 라우트를 나타냅니다. (대부분의 경우에 변경할 일이 없습니다.) + +- `append`는 `current` 라우트에 추가할 수 있도록 합니다 ([`router-link`](router-link.md#props)처럼) + - **router.addRoutes(routes)** > 2.2.0+ diff --git a/docs/kr/api/router-link.md b/docs/kr/api/router-link.md index 68ec19c46..a3d769cfc 100644 --- a/docs/kr/api/router-link.md +++ b/docs/kr/api/router-link.md @@ -26,10 +26,10 @@
    Home - + Home - + Home @@ -38,7 +38,7 @@ User - + Register ``` @@ -99,7 +99,7 @@ 이것의 결과는 ``가 모든 라우터에 대해 활성화 될 것입니다! 링크를 "완전 일치 모드"로 강제하려면 `exact` prop를 사용하십시오. ``` html - + ``` diff --git a/docs/kr/essentials/getting-started.md b/docs/kr/essentials/getting-started.md index b0f6e55ef..4e6ca1d61 100644 --- a/docs/kr/essentials/getting-started.md +++ b/docs/kr/essentials/getting-started.md @@ -4,7 +4,7 @@ Vue.js와 vue-router로 단일 페이지 애플리케이션을 만드는 것은 간단합니다. Vue.js를 통해 우리는 이미 컴포넌트로 애플리케이션을 구성하고 있습니다. vue-router를 추가 할 때, 우리가해야 할 일은 우리의 컴포넌트를 route에 매핑하고 vue-router가 어디서 렌더링할 지 지정하는 것입니다. 다음은 기본적인 예입니다. -> 모든 예제는 Vue의 독립형 버전을 사용하여 템플릿 구문 분석을 가능하게합니다. 자세한 내용은 [여기](http://vuejs.org/guide/installation.html#Standalone-vs-Runtime-only-Build) 있습니다. +> 모든 예제는 Vue의 전체 버전을 사용하여 템플릿 구문 분석을 가능하게합니다. 자세한 내용은 [여기](https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only) 있습니다. ### HTML @@ -17,7 +17,7 @@ Vue.js와 vue-router로 단일 페이지 애플리케이션을 만드는 것은

    - + Go to Foo Go to Bar

    @@ -30,7 +30,7 @@ Vue.js와 vue-router로 단일 페이지 애플리케이션을 만드는 것은 ### JavaScript ``` js -// 0. 모듈 시스템을 사용하는 경우 (예: vue-cli를 이용해서), Vue 및 VueRouter를 가져온 다음 Vue.use(VueRouter)를 호출하십시오. +// 0. 모듈 시스템을 사용하는 경우 (예: vue-cli를 이용해서), Vue 및 VueRouter를 가져온 다음 `Vue.use(VueRouter)`를 호출하십시오. // 1. 라우트 컴포넌트를 정의하십시오. // 다른 파일에서 가져올 수 있습니다. @@ -39,7 +39,7 @@ const Bar = { template: '
    bar
    ' } // 2. 라우트를 정의합니다. // 일부 라우트 정의 각 라우트는 컴포넌트에 맵핑되어야합니다. -// "컴포넌트"는 Vue.extend()를 통해 생성된 +// "컴포넌트"는 `Vue.extend()`를 통해 생성된 // 실제 컴포넌트 생성자이거나 컴포넌트 옵션 객체 일 수 있습니다. // 나중에 중첩 된 라우트에 대해 이야기하겠습니다. const routes = [ diff --git a/docs/kr/essentials/history-mode.md b/docs/kr/essentials/history-mode.md index 8a07fd802..012087c32 100644 --- a/docs/kr/essentials/history-mode.md +++ b/docs/kr/essentials/history-mode.md @@ -40,10 +40,65 @@ location / { } ``` -#### Node.js (Express) +#### Native Node.js + +```js +const http = require("http") +const fs = require("fs") +const httpPort = 80 + +http.createServer((req, res) => { + fs.readFile("index.htm", "utf-8", (err, content) => { + if (err) { + console.log('We cannot open "index.htm" file.') + } + + res.writeHead(200, { + "Content-Type": "text/html; charset=utf-8" + }) + + res.end(content) + }) +}).listen(httpPort, () => { + console.log("Server listening on: http://localhost:%s", httpPort) +}) +``` + + +#### Express와 Node.js Node.js/Express의 경우 [connect-history-api-fallback 미들웨어](https://github.com/bripkens/connect-history-api-fallback)를 고려해보세요. +#### Internet Information Services (IIS) + +``` + + + + + + + + + + + + + + + + + + + + + + + + +``` + + ## 주의 사항 주의 사항이 있습니다. 여러분의 서버는 404 에러를 보고하지 않을 것입니다. 왜냐하면 모든 발견되지 않은 경로가 이제 `index.html` 파일을 제공하기 때문입니다. 이 문제를 해결하려면 Vue 앱에서 catch-all 라우트를 구현하여 404 페이지를 표시해야합니다. @@ -58,4 +113,4 @@ const router = new VueRouter({ }) ``` -또는 Node.js 서버를 사용하는 경우 서버 측의 라우터를 사용하여 들어오는 URL을 일치시키고 라우트가 일치하지 않으면 404로 응답하여 폴백을 구현할 수 있습니다. +또는 Node.js 서버를 사용하는 경우 서버 측의 라우터를 사용하여 들어오는 URL을 일치시키고 라우트가 일치하지 않으면 404로 응답하여 폴백을 구현할 수 있습니다. 더 자세한 설명은 [Vue 서버사이드 렌더링 문서](https://ssr.vuejs.org/en/)을 읽어보세요 diff --git a/docs/kr/essentials/named-routes.md b/docs/kr/essentials/named-routes.md index 525ce3f86..bab81564c 100644 --- a/docs/kr/essentials/named-routes.md +++ b/docs/kr/essentials/named-routes.md @@ -14,7 +14,7 @@ const router = new VueRouter({ }) ``` -명명 된 라우트에 링크하려면, 객체를 `router-link`, 컴포넌트의 `to` prop로 전달할 수 있습니다. +이름을 가진 라우트에 링크하려면, 객체를 `router-link`, 컴포넌트의 `to` prop로 전달할 수 있습니다. ``` html User @@ -22,7 +22,7 @@ const router = new VueRouter({ 이것은 `router.push()`와 프로그램적으로 사용되는 것과 정확히 같은 객체입니다. -``` js +```js router.push({ name: 'user', params: { userId: 123 }}) ``` diff --git a/docs/kr/essentials/passing-props.md b/docs/kr/essentials/passing-props.md index ca1903c69..2ee4b1c03 100644 --- a/docs/kr/essentials/passing-props.md +++ b/docs/kr/essentials/passing-props.md @@ -35,12 +35,12 @@ const router = new VueRouter({ ### Boolean 모드 -props를 true로 설정하면 route.params가 컴포넌트 props로 설정됩니다. +`props`를 `true`로 설정하면 `route.params`가 컴포넌트 `props`로 설정됩니다. ### 객체 모드 -props가 객체일때 컴포넌트 props가 있는 그대로 설정됩니다. -props가 정적일 때 유용합니다. +`props`가 객체일때 컴포넌트 `props`가 있는 그대로 설정됩니다. +`props`가 정적일 때 유용합니다. ``` js const router = new VueRouter({ @@ -52,8 +52,7 @@ const router = new VueRouter({ ### 함수 모드 -props를 반환하는 함수를 만들 수 있습니다. -이를 통해 전달인자를 다른 타입으로 캐스팅하고 적정인 값을 라우트 기반 값과 결합됩니다. +`props`를 반환하는 함수를 만들 수 있습니다. 이를 통해 전달인자를 다른 타입으로 캐스팅하고 적정인 값을 라우트 기반 값과 결합됩니다. ``` js const router = new VueRouter({ @@ -63,9 +62,9 @@ const router = new VueRouter({ }) ``` -`/search?q=vue`는 `{query: "vue"}`를 SearchUser 컴포넌트에 전달합니다. +`/search?q=vue`는 `{query: "vue"}`를 `SearchUser` 컴포넌트에 전달합니다. -라우트 변경시에만 평가되므로 props 함수는 상태를 저장하지 않도록 합니다. -props를 정의할 상태가 필요한 경우 래퍼 컴포넌트를 사용하면 상태가 변경될 때마다 응답할 수 있습니다. +라우트 변경시에만 평가되므로 `props` 함수는 상태를 저장하지 않도록 합니다. +`props`를 정의할 상태가 필요한 경우 래퍼 컴포넌트를 사용하면 상태가 변경될 때마다 응답할 수 있습니다. 고급 사용예를 보려면 [예제](https://github.com/vuejs/vue-router/blob/dev/examples/route-props/app.js)를 확인하세요. diff --git a/docs/ru/SUMMARY.md b/docs/ru/SUMMARY.md index 8abc6c9d5..9eeca8ba6 100644 --- a/docs/ru/SUMMARY.md +++ b/docs/ru/SUMMARY.md @@ -24,9 +24,19 @@ - [Скроллинг](advanced/scroll-behavior.md) - [Ленивая загрузка путей](advanced/lazy-loading.md) - Справочник API - - [router-link](api/router-link.md) - - [router-view](api/router-view.md) - - [Объект route](api/route-object.md) - [Опции конструктора Router'а](api/options.md) - - [Инстанс Router'а](api/router-instance.md) + - [routes](api/options.md#routes) + - [mode](api/options.md#mode) + - [base](api/options.md#base) + - [linkActiveClass](api/options.md#linkactiveclass) + - [linkExactActiveClass](api/options.md#linkexactactiveclass) + - [scrollBehavior](api/options.md#scrollbehavior) + - [parseQuery / stringifyQuery](api/options.md#parsequery--stringifyquery) + - [fallback](api/options.md#fallback) + - [Экземпляр Router](api/router-instance.md) + - [Свойства](api/router-instance.md#свойства) + - [Методы](api/router-instance.md#методы) + - [Объект route](api/route-object.md) - [Интеграция с компонентами Vue](api/component-injections.md) + - [router-link](api/router-link.md) + - [router-view](api/router-view.md) diff --git a/docs/ru/advanced/data-fetching.md b/docs/ru/advanced/data-fetching.md index 6a4482431..aa5e251c9 100644 --- a/docs/ru/advanced/data-fetching.md +++ b/docs/ru/advanced/data-fetching.md @@ -4,7 +4,7 @@ - **Запросив данные после перехода**: сначала перейти к новому пути, затем запросить данные в хуке жизненного цикла целевого компонента. По мере загрузки данных отобразить индикатор состояния загрузки. -- **Запросив данные перед переходом**: запросить данные в сторожевом хуке роутера, и завершить навигацию уже по когда они будут получены. +- **Запросив данные перед переходом**: запросить данные в сторожевом хуке роутера, и завершить навигацию уже когда они будут получены. С технической точки зрения, оба способа годятся — выбор зависит от того, какой UX вы хотите получить. @@ -18,7 +18,7 @@