起因

今天尝试使用webpck的import()来做代码分割。

代码类似如下:

import('./nice-scroll').then(init => init(dom))

结果报错:

ERROR in ./js/utils/auto-set-height.js
Module build failed: SyntaxError: ‘import’ and ‘export’ may only appear at the top level (20:8)

分析

在package.json里面确认了一下,我用的webpack版本是^3.5.4,这个是一定支持import()方法的,那么问题应该就出在babel上了。

先截取我的babel-loader的部分配置:

use: {
    loader: 'babel-loader',
    options: {
        // 不采用.babelrc的配置
        "babelrc": false,
        "presets": [
            ["react"],
            ["es2015"]
        ],
        "plugins": [
            "transform-object-rest-spread",
            "transform-class-properties"
        ]
    }
}

一开始我的猜想是plugin es2015里面的transform-es2015-modules-commonjs先于webpack处理了代码,所以报错。

找了一下禁用的方法,改配置如下:

use: {
    loader: 'babel-loader',
    options: {
        // 不采用.babelrc的配置
        "babelrc": false,
        "presets": [
            ["react"],
            ["es2015", {module: false}]
        ],
        "plugins": [
            "transform-object-rest-spread",
            "transform-class-properties"
        ]
    }
}

还是不行。

后面一直各种关键词的搜索,偶然在github上面找到这个问题Dynamic `import()` crashing in `ModuleConcatenationPlugin`的一个回答:

Nope; babel will always process the code before webpack ever sees it; unless for some reason the file itself is being excluded from being processed by babel-loader.
This error is unrelated to babel; it’s a webpack issue.

这个回答,里面说到在webpack面对的代码都是babel处理过的,这个让我坚信问题还是在babel这块,继续搜索。

意外找到这个这篇文章:代码分离 – 使用import()。里面说到了一个插件:syntax-dynamic-import

Babel官方关于这个插件的描述是:

Syntax only
This plugin only allows Babel to parse this syntax. If you want to transform it then see dynamic-import-webpack or dynamic-import-node.

显然这是一个语法解析的插件,使得babel能够理解dynamic import的语法。再联系上面的报错信息里面的SyntaxError,结果有点明显了。

解决

npm install --save-dev babel-plugin-syntax-dynamic-import

然后调整babel-loader配置如下:

use: {
    loader: 'babel-loader',
    options: {
        // 不采用.babelrc的配置
        "babelrc": false,
        "presets": [
            ["react"],
            ["es2015", { "modules": false }]
        ],
        "plugins": [
            "syntax-dynamic-import",
            "transform-object-rest-spread",
            "transform-class-properties"
        ]
    }
}

重启webpack,顺利通过编译!!!

从一个Electron项目说起

这两天在折腾用Electron来写一个弹幕助手,方便用PC直播的主播能够看到用户发的弹幕并且进行回复和相关管理操作。

主要的开发任务就是对PC站点已有功能的搬迁和调整。

在最后实现退出功能的时候,希望是在菜单栏上有一个退出按钮,用户点击就能直接退出。

下面是mainProcess里面配置菜单的代码,针对退出登录按钮注册了事件,点击的时候,通知对应的web页去实现退出登录功能。

const template = [
    {
        label: '操作',
        submenu: [
            {
                label: '关闭助手',
                role: 'quit'
            },
            {
                label: '退出登录',
                click: function(){
                    mainWindow.webContents.send('main-process-message', '{action: "quit-login"}');
                }
            }
        ]
    }
];

然后是renderProcess这边监听事件并对应处理

const ipc = require('electron').ipcRenderer;
ipc.on('main-process-message', (event, data) => {
    switch(data.action) {
        case "quit-login": 
            user.quit();
            console.log('quit');
            break;
    }
});

一个意外的错误

项目这块,renderProcess的代码都是用webpack进行编译的。

在renderProcess里面引入electron模块之后,立马就报了一个问题:

ERROR in ./~/.1.6.2@electron/index.js
Module not found: Error: Can't resolve 'fs' in '/xxx/electron-quick-start/node_modules/.1.6.2@electron'
@ ./~/.1.6.2@electron/index.js 1:9-22
@ ./js/index.js

上网看了一圈,发现github上的webpack项目里面有人遇到了类似的问题:

Error: Cannot resolve module ‘fs’ with electron and js

然后底下有人给出了解决方式,在webpack.config.js里面加上target: 'electron'就可以的。

试了一下,发现果然是可以的。

深入了解target属性

解决问题之后,出于好奇,就去webpack官网看了target配置的文档

webpack可以为js的各种不同的宿主环境提供编译功能,为了能正确的进行编译,就需要开发人员在配置里面正确的进行配置。

默认情况下,target的值是web,也就是为类浏览器的环境提供编译。

完整的target属性值及对应作用的列表如下:

拷贝自https://github.com/webpack/webpack.js.org/edit/master/content/configuration/target.md

target Description
async-node Compile for usage in a Node.js-like environment (uses fs and vm to load chunks asynchronously)
~~atom~~ Alias for electron-main
~~electron~~ Alias for electron-main
electron-main Compile for Electron for main process.
electron-renderer Compile for Electron for renderer process, providing a target using JsonpTemplatePlugin, FunctionModulePlugin for browser environments and NodeTargetPlugin and ExternalsPlugin for CommonJS and Electron built-in modules.
node Compile for usage in a Node.js-like environment (uses Node.js require to load chunks)
node-webkit Compile for usage in WebKit and uses JSONP for chunk loading. Allows importing of built-in Node.js modules and nw.gui (experimental)
web Compile for usage in a browser-like environment (default)
webworker Compile as WebWorker

所以,把target设为electron,也就能正确的对Electron环境下的代码进行编译了,不过我的代码是写在renderProcess里面的,所以把targets设置为electron-render才是更加合理的选择。