vuex-typescript
vuex-typescript copied to clipboard
After minified the script. Script can't be run on IE11
It showed an error "Vuex handler functions must not be anonymous." If I don't minify the script, it works fine. Other modern browser did work with minified script.
Word around solution for webpack UglifyJSPlugin
Giving the options into UglifyJSPlugin.
The main reason caused the script could not run on IE11 was keep_fnames
. :) I took a long time to test each options in order to make the script works. :( Hope it can save someone life.
new UglifyJSPlugin({ uglifyOptions: { ecma: 5, compress: { keep_fnames: true }, warnings: false, mangle: { keep_fnames: true } }, parallel: 4 })
This doesn't work for me even without minification :/
I think the problem is with IE11 not supporting the name
property on functions which is being used to qualify the store key here.
@AleksueiR It did work on IE11. Please checkout the boilerplate for vuex-typescript. :) Your project may contain a third party npm library that using es6 syntax. https://github.com/Ku4nCheang/vue-testdrive
There are a few things going on here so i'll try to explain it best I can. First off as @AleksueiR mentioned IE11 doesn't support Function.name
with the following syntax:
var test = {
foo: function() { }
};
console.log(test.foo.name); // Doesn't work in IE11
Interestingly IE11 does support Function.name
with the following syntax:
var test = {
foo: function foo() { }
};
console.log(test.foo.name); // Prints 'foo'
Great, it works in at least one case so we can exploit that. Now the next issue is that TS transpiles object shorthand methods like the following:
// TS standard
var test = {
foo() { }
};
// ===> TS compiled to ES5
var test = {
foo: function() { }
};
Which as we saw earlier won't work in IE11. But there is hope. Fortunately Babel takes the following and converts it into what we want:
// ===> TS compiled to ES5
var test = {
foo: function() { }
};
// ===> Babel compiled ES5 to ES5
var test = {
foo: function foo() { }
};
So you can by-pass this issue by setting up your build tool to process TS=>ES5 then have babel make another run over the ES5 generating the named functions that allow the Function.name
to work in IE11. Here's an example for webpack:
{
test: /\.ts$/,
// TS => ATL => ES5 => Babel => ES5
// Currently this is due to vuex-typescript trying to rely on Function.name.
// In our stores we use { f() { } } which generates: { f: function() { } }
// Then vuex-typescript tries do `obj.f.name` which fails in IE11 if you don't have a named function.
// Passing through babel will generate the following: { f: function f() { } }.
use: [
{
loader: 'babel-loader',
query: { plugins: ['transform-runtime'] }
},
'awesome-typescript-loader'
]
},
Note: Babel is configured with preset-es2015
and preset-stage-2
.
@cha55son is right, a function needs to be explicitly named to have its name
property set when it's used in an object literal, and you can do it with an additional Babel pass.
If you don't want or can't modify your build process, it's possible to set the _vuexKey
on functions and it will be used instead of the name
.
[getters, actions, mutations].forEach(dictionary =>
Object.entries(dictionary).forEach(
([name, func]) => ((<any>func)._vuexKey = name)
)
);
Thanks @Ku4nCheang. Using UglifyJSPlugin works as expected and it saved my day.
Hey there,
A little message to contribute to this issue. I am working on a SharePoint SPFX Webpart and my ts.config produces ES5 JS code.
When minified, as expected my code was not working on IE with the "Vuex handler functions must not be anonymous." error. I ran Babel in my gulp build and I could see my getters/mutations/actions look like this in the minified JS:
var test = {
foo: function foo() { }
};
Unfortunaly, it was still not working under IE11 (although it should!).
I managed to make it work under all browsers using the class pattern you can find here: https://github.com/istrib/vuex-typescript/blob/master/src/tests/withModules/store/system/system.ts
Make sure to keep the exact same name between the class function and the one in your mutations/getters/actions otherwise you will meet this kind of error:
[vuex] unknown mutation type
@akhilvvs What is the line of code that raises the mentioned error?
@super2ni below line where i dispatch the action
export const dispatchFetchSomeData = dispatch(actions.fetchSomeData) // this raises an error
Is "personModuleHandlers" in the following code an instance of class personModuleHandlers?
export const FunctionModule = { actions: { fetchSomeData: personModuleHandlers.fetchSomeData, }, }
I would try something like this:
`class PersonModuleHandlers {
@Handler
public async fetchSomeData (state: PersonState) : Promise
export const personModuleHandlers = new PersonModuleHandlers ();
export const FunctionModule = {
actions: {
fetchSomeData: personModuleHandlers.fetchSomeData,
},
}
const actions = PersonModule.actions
export const dispatchFetchSomeData = dispatch(personModuleHandlers .fetchSomeData)
`
@Ku4nCheang can you please paste your webpack config? Your suggestion did not work for me.
@goors Are u sure it is the same issue? Maybe you can try @AleksueiR ‘s method. All u need to do is pasting the code on each store script file.
Just my 2 cents. Anyone still having issues after using @AleksueiR solution make sure to do the _vuexKey handling right after declaring the vuex store module/basket & before exposing the vuex-typescript functions.
For ES5 this will act as object.entries alternative, or you can wrap this in a function and call it.
if (!Object.entries)
Object.entries = function( obj ){
var ownProps = Object.keys( obj ),
i = ownProps.length,
resArray = new Array(i); // preallocate the Array
while (i--)
resArray[i] = [ownProps[i], obj[ownProps[i]]];
return resArray;
};
ref: Object.entries
In Vue CLI you can solve this by adding the following to your vue.config.js
:
configureWebpack: (config) => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer[0].options.uglifyOptions = Object.assign(
{},
config.optimization.minimizer[0].options.uglifyOptions,
{
ecma: 5,
compress: {
keep_fnames: true,
},
warnings: false,
mangle: {
keep_fnames: true,
},
},
);
}
},
Awesome @Gaya , it works for Vue CLI 3.
I also needed to add chainWebpack
, in my case for Vuetify.js.
So this is how my vue.config.js
file ends up looking, in case it is useful as inspiration for someone else after me. :slightly_smiling_face:
module.exports = {
// Fix Vuex-typescript in prod: https://github.com/istrib/vuex-typescript/issues/13#issuecomment-409869231
configureWebpack: (config) => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer[0].options.uglifyOptions = Object.assign(
{},
config.optimization.minimizer[0].options.uglifyOptions,
{
ecma: 5,
compress: {
keep_fnames: true,
},
warnings: false,
mangle: {
keep_fnames: true,
},
},
);
}
},
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => Object.assign(options, {
transformAssetUrls: {
'v-img': ['src', 'lazy-src'],
'v-card': 'src',
'v-card-media': 'src',
'v-responsive': 'src',
}
}));
},
}
In more recent versions of Vue CLI (3.1.0+), they use https://github.com/terser-js/terser instead of uglify-es.
So, to make it work the configuration must be placed in terserOptions
instead of uglifyOptions
.
Here's my new vue.config.js
:
module.exports = {
// Fix Vuex-typescript in prod: https://github.com/istrib/vuex-typescript/issues/13#issuecomment-409869231
configureWebpack: (config) => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer[0].options.terserOptions = Object.assign(
{},
config.optimization.minimizer[0].options.terserOptions,
{
ecma: 5,
compress: {
keep_fnames: true,
},
warnings: false,
mangle: {
keep_fnames: true,
},
},
);
}
},
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => Object.assign(options, {
transformAssetUrls: {
'v-img': ['src', 'lazy-src'],
'v-card': 'src',
'v-card-media': 'src',
'v-responsive': 'src',
}
}));
},
}
I really struggle with this problem, I'm currently on a project that uses vue-typex. I just can't get it to work. I'm not sure what to look for anymore. I've prepared a repro here. I now that vuex-typex is a different package, but maybe somebody here can help me out as well. I'll post the issue on vuex-typex as well, and crosslink it. Thanks
@biohazard999 Work around solution already mentioned. You can either change the minifier settings or add the workaround script in each store file.
@bbzy853 As in the repro I changed the minifier settings, but it does not work for me. I think the babel settings are wrong, but I have no idea how to do the TS->ES5->Babel->ES5 transformation to preserve the function names.
@bbzy853 the strange thing is, it doesn't even work without minification -.- Or I am completely wrong and it is a vuex-typex problem :/
@biohazard999 what kind of error message did you receive? You couldn’t get it works even without minification. Some situations may cause like below:
-
sometimes you adopted an es6 function that supposed not work in IE11. Ployfill for specified function must be used in order to work.
-
use require(“your vue file path”).default, missing “default” will cause Vue renderer does not work. latest Vue compiler need it in order to work.
-
Wepack setting. Make sure it works on Chrome first.
@bbzy853 Thanks for your answer! For the sake of completeness, here is the error message.
SCRIPT5022: Vuex handler functions must not be anonymous. Possible causes: fat-arrow functions, uglify. To fix, pass a unique name as a second parameter after your callback.
@rugia813 gave me the right hint in the vuex-typex repo. In vuex-typex I need to specify the name before exporting: For non IE this works.
commitOpenState: b.commit(setOpenState),
dispatchToggleNavigation: b.dispatch(toggleNavigation),
dispatchCloseNavigation: b.dispatch(closeNavigation),
For IE this needs to be done:
commitOpenState: b.commit(setOpenState, 'commitOpenState'),
dispatchToggleNavigation: b.dispatch(toggleNavigation, 'toggleNavigation'),
dispatchCloseNavigation: b.dispatch(closeNavigation, 'closeNavigation'),
In combination with the webpack terser options:
module.exports = {
// Fix Vuex-typescript in prod: https://github.com/istrib/vuex-typescript/issues/13#issuecomment-409869231
configureWebpack: (config) => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer[0].options.terserOptions = Object.assign(
{},
config.optimization.minimizer[0].options.terserOptions,
{
ecma: 5,
compress: {
keep_fnames: true,
},
warnings: false,
mangle: {
keep_fnames: true,
},
},
);
}
},
}
It works fine. Thanks again for your help!