Это продолжение статей:

> CEF, ES6, Angular 2, TypeScript использование классов .Net Core. Создание кроссплатформенного GUI для .Net с помощью CEF
> CEF, Angular 2 использование событий классов .Net Core

Основная идея этих статей — создание кроссплатформенных приложений на CEF с использованием Angular 2 и .Net Core. Чтобы отвязаться от сервера, используем свежий WebPack и настроим на локальное использование файлов.

Для начала создадим package.json.

Содержимое package.json
{
  "name": "helloapp",
  "version": "1.0.0",
  "scripts": {
    "start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
    "lite": "lite-server",
    "tsc": "tsc",
    "tsc:w": "tsc -w"
  },
  "dependencies": {
    "@angular/common": "~2.4.0",
    "@angular/compiler": "~2.4.0",
    "@angular/core": "~2.4.0",
    "@angular/forms": "~2.4.0",
    "@angular/http": "~2.4.0",
    "@angular/platform-browser": "~2.4.0",
    "@angular/platform-browser-dynamic": "~2.4.0",
    "@angular/router": "~3.4.0",
    "@angular/upgrade": "~2.4.0",
    "angular-in-memory-web-api": "~0.2.4",
    "babel-preset-es2015": "^6.22.0",
    "babel-preset-es2016": "^6.22.0",
    "babel-preset-react": "^6.23.0",
    "bootstrap": "^3.3.7",
    "core-js": "^2.4.1",
    "css-loader": "^0.26.1",
    "jquery": "^3.1.1",
    "postcss-loader": "^1.3.0",
    "raw-loader": "^0.5.1",
    "reflect-metadata": "^0.1.9",
    "rxjs": "^5.1.1",
    "sass-loader": "^6.0.0",
    "style-loader": "^0.13.1",
    "systemjs": "^0.20.7",
    "to-string-loader": "^1.1.5",
    "ts-loader": "^2.0.0",
    "typescript": "^2.1.6",
    "webpack": "^2.2.0",
    "webpack-fail-plugin": "^1.0.5",
    "webpack-notifier": "^1.5.0",
    "zone.js": "^0.7.7"
  },
  "devDependencies": {
    "@types/core-js": "^0.9.35",
    "@types/node": "^6.0.46",
    "babel-core": "6.23.1",
    "babel-eslint": "7.1.1",
    "babel-loader": "6.3.0",
    "babel-plugin-__coverage__": "^11.0.0",
    "babel-polyfill": "^6.0.0",
    "babel-preset-angular2": "^0.0.2",
    "babel-preset-es2015": "^6.22.0",
    "bootstrap-loader": "^2.0.0-beta.20",
    "bootstrap-sass": "^3.3.7",
    "chunk-manifest-webpack-plugin": "^1.0.0",
    "concurrently": "^3.1.0",
    "css-loader": "^0.26.1",
    "css-to-string-loader": "^0.1.2",
    "extract-text-webpack-plugin": "^2.0.0-rc.3",
    "file-loader": "^0.10.0",
    "html-webpack-plugin": "^2.28.0",
    "jquery": "^3.1.1",
    "lite-server": "^2.2.2",
    "node-sass": "^4.5.0",
    "resolve-url-loader": "^1.6.1",
    "sass-loader": "^6.0.0",
    "style-loader": "^0.13.1",
    "typescript": "^2.1.5",
    "url-loader": "^0.5.7"
  }
} 


Здесь собраны загрузчики, ссылки на необходимые файлы итд, которые были подобраны долгими исканиями.

Так же необходим tsconfig.json для компиляции TypeScript-файлов.

Содержимое tsconfig.json
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true,
    "lib": [ "es6", "dom" ],
    "types": [ "node" ],
    "typeRoots": [
      "node_modules/@types"
    ]
  },
  "exclude": [
    "node_modules",
    "wwwroot",
    "**/*.spec.ts"
  ],

  "compileOnSave": true
}


Теперь мы можем создать на основании package.json директорию node_modules с помощью команды npm install из директории приложения.

Прежде чем вызывать WebPack, нужно создать несколько файлов polyfills.ts.

Содержимое polyfills.ts
import 'core-js/es6';
import 'core-js/es7/reflect';
require('zone.js/dist/zone');

if (process.env.ENV === 'production') {
  // Production
} else {
  // Development and test
  Error['stackTraceLimit'] = Infinity;
  require('zone.js/dist/long-stack-trace-zone');
}


Добавить в main.ts ссылки на Bootstap и JQuery:


import 'jquery';
import 'bootstrap-loader';
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/css/bootstrap-theme.css';

Теперь нам доступны стили, glyphicons и тд. Теперь перейдем к самому главному, а именно webpack.config.js, на основании которого и будут собираться нужные нам файлы.

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require("extract-text-webpack-plugin");

var helpers = require('./helpers');
var babelOptions = {
  "presets": [
    "react",
    [
      "es2015",
      {
        "modules": false
      }
    ],
    "es2016"
  ]
};

module.exports = {
  cache: true,
  entry: {
    polyfills: './app/polyfills.ts',
    main: './app/main.ts',
    vendor: [
      'babel-polyfill'
    ]
  },
  output: {
    path: './wwwroot',
    filename: './scripts/[name].js',
    chunkFilename: './scripts/[chunkhash].js',
    publicPath: './'
  },
  module: {
    rules: [{
      test: /\.ts(x?)$/,
      exclude: /node_modules/,
      include: /app/,
      use: [
        {
          loader: 'babel-loader',
          options: babelOptions
         
        },
        {
          loader: 'ts-loader'
        }
      ]
    }, {
      test: /\.js$/,
      exclude: /node_modules/,
      include: /app/,
      use: [
        {
          loader: 'babel-loader',
          options: babelOptions
        }
      ]
    },
      { test: /\.html$/, loader: 'raw-loader', exclude: [helpers.root('app/index.html')] },
      { test: /\.(png|jpg|jpeg|gif|svg)$/, loader: 'url-loader', query: { limit: 25000 } },
      { test: /\.css$/, loader: 'css-to-string-loader!css-loader' },
      { test: /\.scss$/, loaders: ['style', 'css', 'postcss', 'sass'] },
      { test: /\.(woff2?|ttf|eot|svg)$/, loader: 'url-loader?limit=10000' },
      { test: /bootstrap\/dist\/js\/umd\//, loader: 'imports?jQuery=jquery' }


    ]
  },
   resolve: {
       extensions: ['.ts', '.tsx', '.js','.css']
   },
   plugins: [
  // Workaround for angular/angular#11580
  
  new webpack.optimize.CommonsChunkPlugin({
      name: ['app', 'vendor', 'polyfills']
  }),

  new HtmlWebpackPlugin({
      template: 'app/index.html'

  }),
    new ExtractTextPlugin(
        {
            filename: 'styles.css',
            disable: false,
            allChunks: true
        }
),
new webpack.ProvidePlugin({
    jQuery: 'jquery',
    $: 'jquery',
    jquery: 'jquery'
})
   ]
};

Здесь собрано всё, чтобы использовать es6 и всё собиралось в несколько файлов в директории
'./wwwroot'. Теперь можно запустить webpack --config webpack.config.js, который и создаст нужные нам файлы.

Но нам придется подправить выходной index.html. Почему-то для:

<script type="text/javascript" src="././scripts/polyfills.js"></script>
<script type="text/javascript" src="././scripts/vendor.js">
</script><script type="text/javascript" src="././scripts/main.js"></script>

Добавляются лишние ./ — убрав ./, мы можем запустить index.html в любом браузере. Но нас интересует наш cefsimple.exe:

Укажем в адресе файла путь к index.html, например, d:\CEF\CefProgects\TestTypeScript\TestTypeScript\wwwroot\index.html

И Вуаля, мы работаем автономно, без Web-сервера. Если нужно подключиться к серверу, у нас есть все на .Net. Например, .Net Core, WCF и ODATA-клиенты.

Нужно добавить что я отказался от Routing в пользу NgSwitch

Так как в проект построен на использовании systemjs.config.js, то в нем нет поддержки require
который необходим для WebPack, что бы весь Html код интегрировался в js код.

Так как не нашел в TS условную компиляцию, для отладки необходимо закомментировать require

@Component({
    selector: 'nav-menu',
    template:require('./navmenu.component.html'),
    styles: [require('./navmenu.component.css')]

 //   templateUrl: 'app/navmenu/navmenu.component.html',
 //   styleUrls: ['app/navmenu/navmenu.component.css']
})


И раскомментировать


 //   templateUrl: 'app/navmenu/navmenu.component.html',
 //   styleUrls: ['app/navmenu/navmenu.component.css']


исходники и программы и инструкцию использования можно найти в предыдущих статьях в самом низу.
Поделиться с друзьями
-->

Комментарии (12)


  1. Atreides07
    16.02.2017 00:17
    +1

    Спасибо за работу. Жалко что мало плюсов набирают статьи. Но я внимательно слежу за вашими статьями и читаю с большим удовольствием. Пожалуйста, не бросайте.


    1. Serginio1
      16.02.2017 07:44

      Спасибо!


  1. Spoki4
    16.02.2017 07:44

    Добавляются лишние ./

    У вас в publicPath стоит "./" поэтому и добавляется.


    1. Serginio1
      16.02.2017 07:45

      Это понятною Но для файлов ./ это текущая директория. Если её не указать, то необходимые файлы ищутся с корня диска


  1. dmitry_pavlov
    16.02.2017 12:19

    Статья по теме dotnet new angular and dotnet new react


    1. Serginio1
      16.02.2017 12:25

      Не совсем. Это ближе к ASP.NET Core, Angular 2, SignalR для чайников

      Там кстати там WebPack без поддержки Es6

      Хотя они все так или иначе пересекаются. Спасибо!


  1. eletov
    18.02.2017 10:46

    Сергей из исходников не очень понятно как запускать на отличных от windows платформах.


    1. Serginio1
      18.02.2017 11:22

      Да дистрибутивы CEF нужно скачать отсюда
      Chromium Embedded Framework (CEF) Automated Builds

      Кроме того использовать Net Core отсюда
      .Net Core All downloads


  1. Serginio1
    18.02.2017 10:52

    Для этого нужно перекомпилировать под каждую платформу SefSimple
    Плюс я не пробовал на Linux, то возможно надо будет изменить загрузку .Net Core
    Вот моя первая статья Кроссплатформенное использование классов .Net из неуправляемого кода. Или аналог IDispatch на Linux

    На просторах интернета было найдено решение: Hosting .NET Core Clr in your own process и simpleCoreCLRHost и
    initializeCoreCLR createDelegate и
    unixinterface

    Суть подключения заключается в загрузке библиотеки coreclr.dll, получения нужных интерфейсов и запуск CLR Runtime Host.


    1. eletov
      18.02.2017 13:29

      Ясно, спасибо, наверное стоит упомянуть в статье, что пример чисто теоритически кросс-платформен потому что построен на кросс-плафтформенных технологиях, но сама интеграция CEF и .NET Core у вас Windows only. Но в любом случае спасибо за статьи и ссылки, я недавно проектировал что-то похожее и остановился на electron для фронтэнда + .NET Core для интеграции с железом через DllImport, но вся коммуникация была IPC через WebSocket, т.е. приложение состоит из двух процессов. Так же рассматривали edge(для electron), но все же out of process в нашем случае лучше подошёл так как коммуникация простая(по типу событий) и вызовов немного + битность и стабильность процесса electron не страдала если приходится загружать нестабильные нативные DLL для различных железок от вендоров этих железок, так как это делает другой(.NET Core) процесс.


      1. Serginio1
        18.02.2017 13:46

        Спасибо! Суть то в том, что ты первый кто заинтересовался моими разработками.
        Я один. При этом 1С ник, который решил интегрировать .Net в 1С. Ноги растут оттуда.
        Я мог бы сделать и реальную кроссплатформенность, но главное показать как это работает.
        Можно добавить использование JS объектов и функций на стороне .Net.

        Что касается out of process каждый решает сам. Например по опыты в 1С приходится сочетать свои сборки и например тот же AngleSharp проще использовать на стороне 1С итд.
        Можно использовать динамическую компиляцию итд.
        Использовать out of process несколько сложнее.


      1. Serginio1
        18.02.2017 14:29

        Изначально я хотел делать аналог RPC. Его несложно сделать и на существующей модели. По аналогии с .Net Core, обмен с 1C по TCP/IP между различными устройствами

        По сути внешне все остается тем же